@for (branch of job.merkleBranches; track $index) {
-
+ @if ($index === 0 && branch) {
+
+ } @else {
+
+ }
}
@for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss
index 31d12474f..fa94227bd 100644
--- a/frontend/src/app/components/pool/pool.component.scss
+++ b/frontend/src/app/components/pool/pool.component.scss
@@ -220,6 +220,7 @@ div.scrollable {
.merkle {
width: 100px;
+ text-align: center;
}
.empty-branch {
diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts
index 23b795613..4b4b643a2 100644
--- a/frontend/src/app/components/pool/pool.component.ts
+++ b/frontend/src/app/components/pool/pool.component.ts
@@ -361,6 +361,10 @@ export class PoolComponent implements OnInit {
return block.height;
}
+ reverseHash(hash: string) {
+ return hash.match(/../g).reverse().join('');
+ }
+
ngOnDestroy(): void {
this.slugSubscription.unsubscribe();
}
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html
index 08d7fb0ef..41707e37f 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html
@@ -30,7 +30,13 @@
@for (cell of row.merkleCells; track $index) {
-
+ @if ($index === 0 && cell.hash) {
+
+
+
+ } @else {
+
+ }
}
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss
index 6679f2257..15ee074c2 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss
@@ -92,6 +92,16 @@ td {
}
}
}
+
+ .cell-link {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ color: inherit;
+ text-decoration: none;
+ }
}
.badge {
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
index 6f252babe..b28f4ff11 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
@@ -196,6 +196,10 @@ export class StratumList implements OnInit, OnDestroy {
}[type];
}
+ reverseHash(hash: string) {
+ return hash.match(/../g).reverse().join('');
+ }
+
ngOnDestroy(): void {
this.websocketService.stopTrackStratum();
}
From cac62765a18d25dddd1be3e090b4c57fdf2115ad Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 22 Jan 2025 09:10:22 +0000
Subject: [PATCH 037/534] fix stratum tree branch level
---
.../stratum/stratum-list/stratum-list.component.ts | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
index b28f4ff11..481447b07 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
@@ -48,14 +48,16 @@ function parseTag(scriptSig: string): string {
return (ascii.match(/\/.*\//)?.[0] || ascii).trim();
}
-function getMerkleBranchIds(merkleBranches: string[], numBranches: number): string[] {
+function getMerkleBranchIds(merkleBranches: string[], numBranches: number, poolId: number): string[] {
let lastHash = '';
const ids: string[] = [];
for (let i = 0; i < numBranches; i++) {
if (merkleBranches[i]) {
lastHash = merkleBranches[i];
+ ids.push(`${i}-${lastHash}`);
+ } else {
+ ids.push(`${i}-${lastHash}-${poolId}`);
}
- ids.push(`${i}-${lastHash}`);
}
return ids;
}
@@ -98,7 +100,7 @@ export class StratumList implements OnInit, OnDestroy {
const numBranches = Math.max(...Object.values(rawJobs).map(job => job.merkleBranches.length));
const jobs: Record = {};
for (const [id, job] of Object.entries(rawJobs)) {
- jobs[id] = { ...job, tag: parseTag(job.scriptsig), merkleBranchIds: getMerkleBranchIds(job.merkleBranches, numBranches) };
+ jobs[id] = { ...job, tag: parseTag(job.scriptsig), merkleBranchIds: getMerkleBranchIds(job.merkleBranches, numBranches, job.pool) };
}
if (Object.keys(jobs).length === 0) {
return [];
From 2e44ea3f012bf599f468f06849a8d5c2513e4152 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 22 Jan 2025 09:13:44 +0000
Subject: [PATCH 038/534] reorder stratum job table
---
.../stratum-list/stratum-list.component.html | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html
index 41707e37f..24801cf2c 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.html
@@ -7,27 +7,18 @@
- Height
- Reward
- Coinbase Tag
Merkle Branches
Pool
+ Coinbase Tag
+ Reward
+ Height
@for (row of rows; track row.job.pool) {
-
- {{ row.job.height }}
-
-
-
-
-
- {{ row.job.tag }}
-
@for (cell of row.merkleCells; track $index) {
@if ($index === 0 && cell.hash) {
@@ -47,6 +38,15 @@
}
+
+ {{ row.job.tag }}
+
+
+
+
+
+ {{ row.job.height }}
+
}
From 363fa3d8779e07cc9d048234b31cf9d51f60b639 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 22 Jan 2025 09:16:25 +0000
Subject: [PATCH 039/534] improve stratum table layout on mobile
---
.../stratum-list/stratum-list.component.scss | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss
index 15ee074c2..3d274ef2a 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.scss
@@ -104,6 +104,26 @@ td {
}
}
+@media (max-width: 800px) {
+ .stratum-table {
+ td {
+ &.tag {
+ display: none;
+ }
+ }
+ }
+}
+
+@media (max-width: 650px) {
+ .stratum-table {
+ td {
+ &.reward {
+ display: none;
+ }
+ }
+ }
+}
+
.badge {
position: relative;
color: #FFF;
From 3b91a1437aa72d7e0b8c8866fd13c7f0fe839eb4 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 22 Jan 2025 18:58:17 +0900
Subject: [PATCH 040/534] [accelerator] differentiate failed/canceled
accelerations
---
.../accelerations-list/accelerations-list.component.html | 3 ++-
frontend/src/app/interfaces/node-api.interface.ts | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html
index 225bf1955..6756b23e4 100644
--- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html
+++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html
@@ -64,7 +64,8 @@
Pending
Completed ⌛
Mined ⌛
- Canceled ⌛
+ Canceled ⌛
+ Failed ⌛
diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts
index 4d85a938d..05f0855a9 100644
--- a/frontend/src/app/interfaces/node-api.interface.ts
+++ b/frontend/src/app/interfaces/node-api.interface.ts
@@ -412,13 +412,13 @@ export interface Acceleration {
feeDelta: number;
blockHash: string;
blockHeight: number;
-
acceleratedFeeRate?: number;
boost?: number;
bidBoost?: number;
boostCost?: number;
boostRate?: number;
minedByPoolUniqueId?: number;
+ canceled?: number;
}
export interface AccelerationHistoryParams {
From b3f21a10b9a77d7198ca641e4139256301ccf2ef Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Thu, 23 Jan 2025 14:55:31 +0900
Subject: [PATCH 041/534] [accelerator] truncate dashboard title
---
.../accelerations-list/accelerations-list.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html
index 225bf1955..f4f7277ae 100644
--- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html
+++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html
@@ -14,7 +14,7 @@
Requested
- Bid Boost
+ Bid Boost
Block
Pool
Status
From fc4a0f746134bd15e12ad4480573de714be99898 Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Wed, 22 Jan 2025 21:59:15 -0800
Subject: [PATCH 042/534] Fix the missing frontend Stratum config for Docker
builds
---
docker/frontend/entrypoint.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docker/frontend/entrypoint.sh b/docker/frontend/entrypoint.sh
index 2086188c9..9727adb2d 100644
--- a/docker/frontend/entrypoint.sh
+++ b/docker/frontend/entrypoint.sh
@@ -45,6 +45,7 @@ __SERVICES_API__=${SERVICES_API:=https://mempool.space/api/v1/services}
__PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false}
__HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true}
__ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false}
+__STRATUM_ENABLED__=${STRATUM_ENABLED:=false}
# Export as environment variables to be used by envsubst
export __MAINNET_ENABLED__
@@ -76,6 +77,7 @@ export __SERVICES_API__
export __PUBLIC_ACCELERATIONS__
export __HISTORICAL_PRICE__
export __ADDITIONAL_CURRENCIES__
+export __STRATUM_ENABLED__
folder=$(find /var/www/mempool -name "config.js" | xargs dirname)
echo ${folder}
From a62a3cc774093b9e23e3b31be37a32a93c345a51 Mon Sep 17 00:00:00 2001
From: "transifex-integration[bot]"
<43880903+transifex-integration[bot]@users.noreply.github.com>
Date: Thu, 23 Jan 2025 06:29:40 +0000
Subject: [PATCH 043/534] Translate frontend/src/locale/messages.xlf in fr
100% reviewed source file: 'frontend/src/locale/messages.xlf'
on 'fr'.
---
frontend/src/locale/messages.fr.xlf | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/frontend/src/locale/messages.fr.xlf b/frontend/src/locale/messages.fr.xlf
index c193f296b..aac7eead9 100644
--- a/frontend/src/locale/messages.fr.xlf
+++ b/frontend/src/locale/messages.fr.xlf
@@ -1567,7 +1567,7 @@
Total Bid Boost
- Augmentation totale des frais
+ Total frais ajoutés
src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html
11
@@ -1728,7 +1728,7 @@
Bid Boost
- Augmentation des frais
+ Frais ajoutés
src/app/components/acceleration/accelerations-list/accelerations-list.component.html
17
@@ -6739,7 +6739,7 @@
Just now
- Juste maintenant
+ À l'instant
src/app/components/time/time.component.ts
111
From e6965dac80fe937bfd3a7189fc98ce24f63c9c65 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Thu, 23 Jan 2025 18:00:27 +0900
Subject: [PATCH 044/534] [accelerator] fix sca card on file
---
.../accelerate-checkout/accelerate-checkout.component.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
index f15faccf7..2a4d681d9 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
@@ -757,9 +757,9 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
billing: {
givenName: assumedGivenName,
familyName: assumedFamilyName,
- addressLines: [cardOnFile.card.billing.addressLine1],
- city: cardOnFile.card.billing.locality,
- state: cardOnFile.card.billing.administrativeDistrictLevel1,
+ addressLines: [cardOnFile.card.billing.addressLine1 ?? ''],
+ city: cardOnFile.card.billing.locality ?? '',
+ state: cardOnFile.card.billing.administrativeDistrictLevel1 ?? '',
countyCode: cardOnFile.card.billing.country,
}
}
From 24d0ed4ced4cd0fb8e32b39e94c0225b326290ad Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Fri, 24 Jan 2025 01:56:15 +0000
Subject: [PATCH 045/534] fix next block merkle row layout
---
.../src/app/components/pool/pool.component.html | 12 +++++++-----
.../src/app/components/pool/pool.component.scss | 13 +++++++++++++
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html
index f98794b68..35bad3af7 100644
--- a/frontend/src/app/components/pool/pool.component.html
+++ b/frontend/src/app/components/pool/pool.component.html
@@ -267,11 +267,13 @@
@for (branch of job.merkleBranches; track $index) {
- @if ($index === 0 && branch) {
-
- } @else {
-
- }
+
+ @if ($index === 0 && branch) {
+
+
+
+ }
+
}
@for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss
index fa94227bd..26e6008b6 100644
--- a/frontend/src/app/components/pool/pool.component.scss
+++ b/frontend/src/app/components/pool/pool.component.scss
@@ -241,6 +241,19 @@ div.scrollable {
td {
position: relative;
height: 2em;
+
+ .cell-link {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ color: inherit;
+ text-decoration: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
}
}
From b826227b3626f383247a18bcc6c6d9774f41306b Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Sat, 25 Jan 2025 15:46:21 +0900
Subject: [PATCH 046/534] [services] twitter -> X
---
frontend/src/app/components/faucet/faucet.component.html | 8 ++++----
.../github-login.component/github-login.component.html | 4 ++--
.../components/twitter-login/twitter-login.component.html | 4 ++--
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/frontend/src/app/components/faucet/faucet.component.html b/frontend/src/app/components/faucet/faucet.component.html
index 19d76d9dd..dbe0f25ea 100644
--- a/frontend/src/app/components/faucet/faucet.component.html
+++ b/frontend/src/app/components/faucet/faucet.component.html
@@ -19,10 +19,10 @@
} @else if (!user) {
-
-
To use the faucet, please
+
+ To use the faucet, please
-
+
}
@else if (user && user.status === 'pending' && !user.email && user.snsId) {
@@ -39,7 +39,7 @@
To use the faucet, please
-
+
}
@else if (error === 'account_limited') {
diff --git a/frontend/src/app/components/github-login.component/github-login.component.html b/frontend/src/app/components/github-login.component/github-login.component.html
index de9c743b5..e57019a26 100644
--- a/frontend/src/app/components/github-login.component/github-login.component.html
+++ b/frontend/src/app/components/github-login.component/github-login.component.html
@@ -1,6 +1,6 @@
-
+ {{ buttonString }}
+
- {{ buttonString }}
\ No newline at end of file
diff --git a/frontend/src/app/components/twitter-login/twitter-login.component.html b/frontend/src/app/components/twitter-login/twitter-login.component.html
index 6ff40bd50..5e687341c 100644
--- a/frontend/src/app/components/twitter-login/twitter-login.component.html
+++ b/frontend/src/app/components/twitter-login/twitter-login.component.html
@@ -1,6 +1,6 @@
-
+ style="background-color: rgb(31, 35, 40)" [style]="width ? 'width: ' + width : ''">
{{ buttonString }}
+
From 9e8a35f4a94dd8ce8faa399b53431d5c709639c8 Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Sat, 25 Jan 2025 18:00:45 -0800
Subject: [PATCH 047/534] Update rust to 1.83
---
rust/gbt/rust-toolchain | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/gbt/rust-toolchain b/rust/gbt/rust-toolchain
index 17420a571..74c280fb8 100644
--- a/rust/gbt/rust-toolchain
+++ b/rust/gbt/rust-toolchain
@@ -1 +1 @@
-1.79
+1.83
From a074c4b2c3a1710b73e82bb7019bd09a041ff450 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Sun, 26 Jan 2025 11:26:27 +0900
Subject: [PATCH 048/534] [accelerator] add credit card provider fa icons
---
.../accelerate-checkout.component.html | 8 +++-
.../svg-images/svg-images.component.html | 43 +++++++++++++++++++
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
index de6c71947..4594fd9fc 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
@@ -427,8 +427,12 @@
@if (canPayWithCardOnFile) {
@if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { }
-
-
{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.brand }} {{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}
+ @if (['VISA', 'MASTERCARD', 'JCB', 'DISCOVER', 'DISCOVER_DINERS', 'AMERICAN_EXPRESS'].includes(estimate?.availablePaymentMethods?.cardOnFile?.card?.brand)) {
+
+ } @else {
+
+ }
+
{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}
}
diff --git a/frontend/src/app/components/svg-images/svg-images.component.html b/frontend/src/app/components/svg-images/svg-images.component.html
index 34ed23bd0..76aa3de85 100644
--- a/frontend/src/app/components/svg-images/svg-images.component.html
+++ b/frontend/src/app/components/svg-images/svg-images.component.html
@@ -1,4 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 0674f3a3eec9e2d3af56238a4f8a90ee64f17923 Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Sun, 26 Jan 2025 10:03:47 -0800
Subject: [PATCH 049/534] Update Rust to 1.84
---
rust/gbt/Cargo.lock | 189 ++++++++++++++++++----------------------
rust/gbt/rust-toolchain | 2 +-
2 files changed, 84 insertions(+), 107 deletions(-)
diff --git a/rust/gbt/Cargo.lock b/rust/gbt/Cargo.lock
index 00c755589..b0d905884 100644
--- a/rust/gbt/Cargo.lock
+++ b/rust/gbt/Cargo.lock
@@ -1,21 +1,21 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "addr2line"
-version = "0.22.0"
+version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aho-corasick"
@@ -28,48 +28,42 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
-version = "0.3.73"
+version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
- "cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
+ "windows-targets",
]
[[package]]
name = "bitflags"
-version = "2.6.0"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "bytemuck"
-version = "1.16.1"
+version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]]
name = "bytes"
-version = "1.6.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
-
-[[package]]
-name = "cc"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]]
name = "cfg-if"
@@ -88,9 +82,9 @@ dependencies = [
[[package]]
name = "ctor"
-version = "0.2.8"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
+checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
dependencies = [
"quote",
"syn",
@@ -119,27 +113,21 @@ dependencies = [
[[package]]
name = "gimli"
-version = "0.29.0"
+version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "hashbrown"
-version = "0.14.5"
+version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "indexmap"
-version = "2.2.6"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
dependencies = [
"equivalent",
"hashbrown",
@@ -153,15 +141,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.155"
+version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
-version = "0.8.4"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets",
@@ -169,9 +157,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.22"
+version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "matchers"
@@ -190,18 +178,18 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
-version = "0.7.4"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
dependencies = [
- "adler",
+ "adler2",
]
[[package]]
name = "napi"
-version = "2.16.8"
+version = "2.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1bd081bbaef43600fd2c5dd4c525b8ecea7dfdacf40ebc674e87851dce6559e"
+checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b"
dependencies = [
"bitflags",
"ctor",
@@ -213,15 +201,15 @@ dependencies = [
[[package]]
name = "napi-build"
-version = "2.1.3"
+version = "2.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
+checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19"
[[package]]
name = "napi-derive"
-version = "2.16.9"
+version = "2.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87c3b5d4ab13e20a4bb9d3a1e2f3d4e77eee4a205d0f810abfd226b971dc6ce5"
+checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c"
dependencies = [
"cfg-if",
"convert_case",
@@ -233,9 +221,9 @@ dependencies = [
[[package]]
name = "napi-derive-backend"
-version = "1.0.71"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96de436a6ab93265beef838f8333c8345438f059df6081fe0ad0b8648ee0c524"
+checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf"
dependencies = [
"convert_case",
"once_cell",
@@ -265,30 +253,20 @@ dependencies = [
"winapi",
]
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
[[package]]
name = "object"
-version = "0.36.1"
+version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "overload"
@@ -298,15 +276,15 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pin-project-lite"
-version = "0.2.14"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "priority-queue"
-version = "2.0.3"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70c501afe3a2e25c9bd219aa56ec1e04cdb3fcdd763055be268778c13fa82c1f"
+checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d"
dependencies = [
"autocfg",
"equivalent",
@@ -315,32 +293,32 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.36"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
-version = "1.10.5"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.7",
- "regex-syntax 0.8.4",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
]
[[package]]
@@ -354,13 +332,13 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.4",
+ "regex-syntax 0.8.5",
]
[[package]]
@@ -371,9 +349,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustc-demangle"
@@ -383,9 +361,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "semver"
-version = "1.0.23"
+version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
[[package]]
name = "sharded-slab"
@@ -404,9 +382,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
-version = "2.0.71"
+version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
@@ -425,20 +403,19 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.38.1"
+version = "1.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
+checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
dependencies = [
"backtrace",
- "num_cpus",
"pin-project-lite",
]
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
@@ -447,9 +424,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.27"
+version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
@@ -458,9 +435,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.32"
+version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
@@ -479,9 +456,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
-version = "0.3.18"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"matchers",
"nu-ansi-term",
@@ -497,21 +474,21 @@ dependencies = [
[[package]]
name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
[[package]]
name = "unicode-segmentation"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "valuable"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "winapi"
diff --git a/rust/gbt/rust-toolchain b/rust/gbt/rust-toolchain
index 74c280fb8..40671b908 100644
--- a/rust/gbt/rust-toolchain
+++ b/rust/gbt/rust-toolchain
@@ -1 +1 @@
-1.83
+1.84
From 60d548df4693bb3cf0a91babfdb03ab472d35d77 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Mon, 27 Jan 2025 03:37:01 +0000
Subject: [PATCH 050/534] add missing sg1 hnl emojis
---
.../app/components/server-health/server-health.component.ts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/frontend/src/app/components/server-health/server-health.component.ts b/frontend/src/app/components/server-health/server-health.component.ts
index 6f92c0c93..c1b6fe443 100644
--- a/frontend/src/app/components/server-health/server-health.component.ts
+++ b/frontend/src/app/components/server-health/server-health.component.ts
@@ -82,6 +82,10 @@ export class ServerHealthComponent implements OnInit {
return '🇺🇸';
} else if (host.includes('.va1.')) {
return '🇺🇸';
+ } else if (host.includes('.sg1.')) {
+ return '🇸🇬';
+ } else if (host.includes('.hnl.')) {
+ return '🤙';
} else {
return '';
}
From edc5593792789a9e917d31ddf24aaf8016ded7ef Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Tue, 28 Jan 2025 07:57:19 +0100
Subject: [PATCH 051/534] [services] remove image md5 in urls
---
frontend/src/app/components/about/about.component.html | 4 ++--
.../components/master-page/master-page.component.html | 9 ++++-----
.../components/master-page/master-page.component.scss | 2 +-
.../src/app/components/tracker/tracker.component.html | 2 +-
4 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html
index 40d6e1914..433fe1abb 100644
--- a/frontend/src/app/components/about/about.component.html
+++ b/frontend/src/app/components/about/about.component.html
@@ -217,7 +217,7 @@
-
+
@@ -229,7 +229,7 @@
diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html
index 557529eef..436cf3c67 100644
--- a/frontend/src/app/components/master-page/master-page.component.html
+++ b/frontend/src/app/components/master-page/master-page.component.html
@@ -4,9 +4,8 @@
-
-
-
+
+
@@ -23,7 +22,7 @@
} @else {
-
+
@@ -43,7 +42,7 @@
} @else {
-
+
diff --git a/frontend/src/app/components/master-page/master-page.component.scss b/frontend/src/app/components/master-page/master-page.component.scss
index 6bd4fd821..8960b0b05 100644
--- a/frontend/src/app/components/master-page/master-page.component.scss
+++ b/frontend/src/app/components/master-page/master-page.component.scss
@@ -269,7 +269,7 @@ nav {
text-align: center;
align-self: center;
cursor: pointer;
- &.anon {
+ .anon {
border: 1.5px solid lightgrey;
color: lightgrey;
border-radius: 5px;
diff --git a/frontend/src/app/components/tracker/tracker.component.html b/frontend/src/app/components/tracker/tracker.component.html
index e07898da3..1eef206d1 100644
--- a/frontend/src/app/components/tracker/tracker.component.html
+++ b/frontend/src/app/components/tracker/tracker.component.html
@@ -8,7 +8,7 @@
} @else if (enterpriseInfo?.img || enterpriseInfo?.imageMd5) {
-
+
}
From ec7af86142acd7dcd0f2b5f0970c2e61a9e0dd13 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 28 Jan 2025 12:47:11 +0100
Subject: [PATCH 052/534] Add debug.log path to mainnet prod config
---
production/mempool-config.mainnet.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json
index 0cb682713..b14e3cd07 100644
--- a/production/mempool-config.mainnet.json
+++ b/production/mempool-config.mainnet.json
@@ -30,7 +30,8 @@
"CORE_RPC": {
"PORT": 8332,
"USERNAME": "__BITCOIN_RPC_USER__",
- "PASSWORD": "__BITCOIN_RPC_PASS__"
+ "PASSWORD": "__BITCOIN_RPC_PASS__",
+ "DEBUG_LOG_PATH": "/bitcoin/debug.log"
},
"SECOND_CORE_RPC": {
"PORT": 8302,
From 2407cbfd9adda8a8ea5f9ef871593da2b30e621b Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 28 Jan 2025 12:55:28 +0100
Subject: [PATCH 053/534] Enable microsecond logging in bitcoin.conf
---
production/bitcoin.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/production/bitcoin.conf b/production/bitcoin.conf
index 57d993eb4..8fe17d921 100644
--- a/production/bitcoin.conf
+++ b/production/bitcoin.conf
@@ -15,6 +15,7 @@ whitelist=127.0.0.1
whitelist=103.99.168.0/22
whitelist=2401:b140::/32
blocksxor=0
+logtimemicros=1
#uacomment=@wiz
[main]
From f54bccf267ff3e4b75a0875218a4be208ebca6b9 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Tue, 28 Jan 2025 18:33:50 +0100
Subject: [PATCH 054/534] [accelerator] fix cashapp acceleration
---
.../accelerate-checkout/accelerate-checkout.component.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
index f4f9fbbc2..ac6c7f147 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
@@ -211,7 +211,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
if (this.timeoutTimer) {
clearTimeout(this.timeoutTimer);
}
- if (!this.estimate && ['quote', 'summary', 'checkout'].includes(this.step)) {
+ if (!this.estimate && ['quote', 'summary', 'checkout', 'processing'].includes(this.step)) {
this.fetchEstimate();
}
if (this._step === 'checkout') {
@@ -838,7 +838,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
const redirectHostname = document.location.hostname === 'localhost' ? `http://localhost:4200`: `https://${document.location.hostname}`;
- const costUSD = this.step === 'processing' ? 69.69 : (this.cost / 100_000_000 * conversions.USD); // When we're redirected to this component, the payment data is already linked to the payment token, so does not matter what amonut we put in there, therefore it's 69.69
+ const costUSD = this.cost / 100_000_000 * conversions.USD;
const paymentRequest = this.payments.paymentRequest({
countryCode: 'US',
currencyCode: 'USD',
From bb5b7711285dbea3a1c77ed74ea9615be99a2db9 Mon Sep 17 00:00:00 2001
From: wiz
Date: Sat, 1 Feb 2025 01:51:44 +0900
Subject: [PATCH 055/534] ops: Tweak nginx cache limits for apinormal
---
production/nginx/http-proxy-cache.conf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/production/nginx/http-proxy-cache.conf b/production/nginx/http-proxy-cache.conf
index a14d519a4..098f7831c 100644
--- a/production/nginx/http-proxy-cache.conf
+++ b/production/nginx/http-proxy-cache.conf
@@ -2,7 +2,7 @@
proxy_cache_path /var/cache/nginx/services keys_zone=services:200m levels=1:2 inactive=30d max_size=200m;
proxy_cache_path /var/cache/nginx/apihot keys_zone=apihot:200m levels=1:2 inactive=60m max_size=20m;
proxy_cache_path /var/cache/nginx/apiwarm keys_zone=apiwarm:200m levels=1:2 inactive=24h max_size=200m;
-proxy_cache_path /var/cache/nginx/apinormal keys_zone=apinormal:200m levels=1:2 inactive=30d max_size=2000m;
+proxy_cache_path /var/cache/nginx/apinormal keys_zone=apinormal:500m levels=1:2 inactive=24h max_size=2000m;
proxy_cache_path /var/cache/nginx/apicold keys_zone=apicold:200m levels=1:2 inactive=60d max_size=2000m;
proxy_cache_path /var/cache/nginx/unfurler keys_zone=unfurler:200m levels=1:2 inactive=30d max_size=2000m;
From aad4783415f791ec50ef8fb8989a4312d05a91fe Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Tue, 4 Feb 2025 21:33:21 +0100
Subject: [PATCH 056/534] fix cors
---
backend/src/index.ts | 3 +++
1 file changed, 3 insertions(+)
diff --git a/backend/src/index.ts b/backend/src/index.ts
index dc6a8ae1a..3cc73afb7 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -131,6 +131,9 @@ class Server {
this.app
.use((req: Request, res: Response, next: NextFunction) => {
res.setHeader('Access-Control-Allow-Origin', '*');
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
+ res.setHeader('Access-Control-Allow-Headers', 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With');
+ res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count,X-Mempool-Auth');
next();
})
.use(express.urlencoded({ extended: true }))
From a664f0c2305e56885940ac4adfe5b609ec504d87 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Thu, 6 Feb 2025 12:39:02 +0100
Subject: [PATCH 057/534] set content limit to 10mb
---
backend/src/index.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/backend/src/index.ts b/backend/src/index.ts
index dc6a8ae1a..834903a6d 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -133,9 +133,9 @@ class Server {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
})
- .use(express.urlencoded({ extended: true }))
- .use(express.text({ type: ['text/plain', 'application/base64'] }))
- .use(express.json())
+ .use(express.urlencoded({ extended: true, limit: '10mb' }))
+ .use(express.text({ type: ['text/plain', 'application/base64'], limit: '10mb' }))
+ .use(express.json({ limit: '10mb' }))
;
if (config.DATABASE.ENABLED && config.FIAT_PRICE.ENABLED) {
From 5e0cbb084a391659b5872e090a54b55873d620c0 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Fri, 7 Feb 2025 11:34:18 +0100
Subject: [PATCH 058/534] add new fa icon
---
frontend/src/app/shared/shared.module.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts
index 76184113f..d937e6bbb 100644
--- a/frontend/src/app/shared/shared.module.ts
+++ b/frontend/src/app/shared/shared.module.ts
@@ -7,7 +7,7 @@ import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, fa
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft,
faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck,
faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline,
- faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes, faCreditCard } from '@fortawesome/free-solid-svg-icons';
+ faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes, faCreditCard, faMicroscope } from '@fortawesome/free-solid-svg-icons';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { MenuComponent } from '@components/menu/menu.component';
import { PreviewTitleComponent } from '@components/master-page-preview/preview-title.component';
@@ -464,5 +464,6 @@ export class SharedModule {
library.addIcons(faRobot);
library.addIcons(faShareNodes);
library.addIcons(faCreditCard);
+ library.addIcons(faMicroscope);
}
}
From 1779c672e359b0d678f49a139d64becdaccfcdf4 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Fri, 7 Feb 2025 16:52:46 +0100
Subject: [PATCH 059/534] Don't tweak scrollLeft if time is left to right
---
.../app/components/start/start.component.ts | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts
index 31317cab5..7db1a75e1 100644
--- a/frontend/src/app/components/start/start.component.ts
+++ b/frontend/src/app/components/start/start.component.ts
@@ -194,14 +194,16 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy {
applyScrollLeft(): void {
if (this.blockchainContainer?.nativeElement?.scrollWidth) {
let lastScrollLeft = null;
- while (this.scrollLeft < 0 && this.shiftPagesForward() && lastScrollLeft !== this.scrollLeft) {
- lastScrollLeft = this.scrollLeft;
- this.scrollLeft += this.pageWidth;
- }
- lastScrollLeft = null;
- while (this.scrollLeft > this.blockchainContainer.nativeElement.scrollWidth && this.shiftPagesBack() && lastScrollLeft !== this.scrollLeft) {
- lastScrollLeft = this.scrollLeft;
- this.scrollLeft -= this.pageWidth;
+ if (!this.timeLtr) {
+ while (this.scrollLeft < 0 && this.shiftPagesForward() && lastScrollLeft !== this.scrollLeft) {
+ lastScrollLeft = this.scrollLeft;
+ this.scrollLeft += this.pageWidth;
+ }
+ lastScrollLeft = null;
+ while (this.scrollLeft > this.blockchainContainer.nativeElement.scrollWidth && this.shiftPagesBack() && lastScrollLeft !== this.scrollLeft) {
+ lastScrollLeft = this.scrollLeft;
+ this.scrollLeft -= this.pageWidth;
+ }
}
this.blockchainContainer.nativeElement.scrollLeft = this.scrollLeft;
}
From 3691eda9d1311c49a3e4f9822df56c3d33995167 Mon Sep 17 00:00:00 2001
From: hunicus <93150691+hunicus@users.noreply.github.com>
Date: Sat, 8 Feb 2025 14:40:04 -0500
Subject: [PATCH 060/534] Add fortris to enterprise sponsors
---
frontend/src/app/components/about/about.component.html | 9 +++++++++
frontend/src/app/components/about/about.component.scss | 4 ++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html
index 433fe1abb..51dad662f 100644
--- a/frontend/src/app/components/about/about.component.html
+++ b/frontend/src/app/components/about/about.component.html
@@ -146,6 +146,15 @@
Bull Bitcoin
+
+
+
+
+
+
+
+ Fortris
+
diff --git a/frontend/src/app/components/about/about.component.scss b/frontend/src/app/components/about/about.component.scss
index 6a76bf299..e756473a6 100644
--- a/frontend/src/app/components/about/about.component.scss
+++ b/frontend/src/app/components/about/about.component.scss
@@ -264,6 +264,6 @@
display: flex;
flex-wrap: wrap;
justify-content: center;
- max-width: 800px;
+ max-width: 850px;
}
-}
\ No newline at end of file
+}
From 6340dc571c6b5ae99eaadd275beddbe49502c3f2 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Sun, 9 Feb 2025 18:13:06 +0100
Subject: [PATCH 061/534] Don't show ETA on unbroadcasted txs, and placeholder
for missing fee
---
.../transaction-details.component.html | 8 +++----
.../transaction-details.component.ts | 1 +
.../transaction-raw.component.html | 4 +---
.../transaction/transaction-raw.component.ts | 23 +------------------
4 files changed, 7 insertions(+), 29 deletions(-)
diff --git a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html
index c5609882c..78bba955c 100644
--- a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html
+++ b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html
@@ -153,7 +153,7 @@
@if (!isLoadingTx) {
- @if (!replaced && !isCached) {
+ @if (!replaced && !isCached && !unbroadcasted) {
ETA
@@ -184,7 +184,7 @@
}
- } @else {
+ } @else if (!unbroadcasted){
}
@@ -213,11 +213,11 @@
@if (!isLoadingTx) {
Fee
- {{ tx.fee | number }} sats
+ {{ (tx.fee | number) ?? '-' }} sats
@if (isAcceleration && accelerationInfo?.bidBoost ?? tx.feeDelta > 0) {
+{{ accelerationInfo?.bidBoost ?? tx.feeDelta | number }} sats
}
-
+ = 0" [blockConversion]="tx.price" [value]="tx.fee + (isAcceleration ? ((accelerationInfo?.bidBoost ?? tx.feeDelta) || 0) : 0)">
} @else {
diff --git a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.ts b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.ts
index 2b539c154..c6260da48 100644
--- a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.ts
+++ b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.ts
@@ -38,6 +38,7 @@ export class TransactionDetailsComponent implements OnInit {
@Input() replaced: boolean;
@Input() isCached: boolean;
@Input() ETA$: Observable;
+ @Input() unbroadcasted: boolean;
@Output() accelerateClicked = new EventEmitter();
@Output() toggleCpfp$ = new EventEmitter();
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html
index b6286779a..450e18ecd 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.html
+++ b/frontend/src/app/components/transaction/transaction-raw.component.html
@@ -58,6 +58,7 @@
}
;
mempoolBlocksSubscription: Subscription;
constructor(
public route: ActivatedRoute,
public router: Router,
public stateService: StateService,
- public etaService: EtaService,
public electrsApi: ElectrsApiService,
public websocketService: WebsocketService,
public formBuilder: UntypedFormBuilder,
@@ -195,24 +192,6 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
});
this.setGraphSize();
- this.ETA$ = combineLatest([
- this.stateService.mempoolBlocks$.pipe(startWith(null)),
- this.stateService.difficultyAdjustment$.pipe(startWith(null)),
- ]).pipe(
- map(([mempoolBlocks, da]) => {
- return this.etaService.calculateETA(
- this.stateService.network,
- this.transaction,
- mempoolBlocks,
- null,
- da,
- null,
- null,
- null
- );
- })
- );
-
this.mempoolBlocksSubscription = this.stateService.mempoolBlocks$.subscribe(() => {
if (this.transaction) {
this.stateService.markBlock$.next({
From 27c28f939c31e50c3aad0dc4e014dab3ea4ecfec Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Mon, 10 Feb 2025 03:47:05 +0000
Subject: [PATCH 062/534] misc unfurl preview fixes
---
.../address/address-preview.component.ts | 10 +++---
.../block/block-preview.component.html | 2 +-
.../block/block-preview.component.ts | 23 ++++++++------
.../components/pool/pool-preview.component.ts | 28 +++++++++--------
.../transaction-preview.component.ts | 20 ++++++------
.../wallet/wallet-preview.component.ts | 20 ++++++------
.../channel/channel-preview.component.ts | 14 +++++----
.../group/group-preview.component.ts | 18 ++++++-----
.../lightning/node/node-preview.component.ts | 14 +++++----
.../nodes-per-isp-preview.component.ts | 14 +++++----
.../src/app/services/opengraph.service.ts | 31 ++++++++++++-------
11 files changed, 111 insertions(+), 83 deletions(-)
diff --git a/frontend/src/app/components/address/address-preview.component.ts b/frontend/src/app/components/address/address-preview.component.ts
index bcc328787..1106d6096 100644
--- a/frontend/src/app/components/address/address-preview.component.ts
+++ b/frontend/src/app/components/address/address-preview.component.ts
@@ -36,6 +36,8 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
sent = 0;
totalUnspent = 0;
+ ogSession: number;
+
constructor(
private route: ActivatedRoute,
private electrsApiService: ElectrsApiService,
@@ -58,7 +60,7 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
.pipe(
switchMap((params: ParamMap) => {
this.rawAddress = params.get('id') || '';
- this.openGraphService.waitFor('address-data-' + this.rawAddress);
+ this.ogSession = this.openGraphService.waitFor('address-data-' + this.rawAddress);
this.error = undefined;
this.isLoadingAddress = true;
this.loadedConfirmedTxCount = 0;
@@ -79,7 +81,7 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
this.isLoadingAddress = false;
this.error = err;
console.log(err);
- this.openGraphService.fail('address-data-' + this.rawAddress);
+ this.openGraphService.fail({ event: 'address-data-' + this.rawAddress, sessionId: this.ogSession });
return of(null);
})
);
@@ -97,7 +99,7 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
this.address = address;
this.updateChainStats();
this.isLoadingAddress = false;
- this.openGraphService.waitOver('address-data-' + this.rawAddress);
+ this.openGraphService.waitOver({ event: 'address-data-' + this.rawAddress, sessionId: this.ogSession });
})
)
.subscribe(() => {},
@@ -105,7 +107,7 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
console.log(error);
this.error = error;
this.isLoadingAddress = false;
- this.openGraphService.fail('address-data-' + this.rawAddress);
+ this.openGraphService.fail({ event: 'address-data-' + this.rawAddress, sessionId: this.ogSession });
}
);
}
diff --git a/frontend/src/app/components/block/block-preview.component.html b/frontend/src/app/components/block/block-preview.component.html
index 036ab8399..6ea8e3387 100644
--- a/frontend/src/app/components/block/block-preview.component.html
+++ b/frontend/src/app/components/block/block-preview.component.html
@@ -49,7 +49,7 @@
-
+
Miner
diff --git a/frontend/src/app/components/block/block-preview.component.ts b/frontend/src/app/components/block/block-preview.component.ts
index 42a47f3c4..f5b31e846 100644
--- a/frontend/src/app/components/block/block-preview.component.ts
+++ b/frontend/src/app/components/block/block-preview.component.ts
@@ -35,6 +35,8 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
overviewSubscription: Subscription;
networkChangedSubscription: Subscription;
+ ogSession: number;
+
@ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent;
constructor(
@@ -53,8 +55,8 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
const block$ = this.route.paramMap.pipe(
switchMap((params: ParamMap) => {
this.rawId = params.get('id') || '';
- this.openGraphService.waitFor('block-viz-' + this.rawId);
- this.openGraphService.waitFor('block-data-' + this.rawId);
+ this.ogSession = this.openGraphService.waitFor('block-viz-' + this.rawId);
+ this.ogSession = this.openGraphService.waitFor('block-data-' + this.rawId);
const blockHash: string = params.get('id') || '';
this.block = undefined;
@@ -86,8 +88,8 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
catchError((err) => {
this.error = err;
this.seoService.logSoft404();
- this.openGraphService.fail('block-data-' + this.rawId);
- this.openGraphService.fail('block-viz-' + this.rawId);
+ this.openGraphService.fail({ event: 'block-data-' + this.rawId, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'block-viz-' + this.rawId, sessionId: this.ogSession });
return of(null);
}),
);
@@ -114,7 +116,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
this.isLoadingOverview = true;
this.overviewError = null;
- this.openGraphService.waitOver('block-data-' + this.rawId);
+ this.openGraphService.waitOver({ event: 'block-data-' + this.rawId, sessionId: this.ogSession });
}),
throttleTime(50, asyncScheduler, { leading: true, trailing: true }),
shareReplay({ bufferSize: 1, refCount: true })
@@ -129,7 +131,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
.pipe(
catchError((err) => {
this.overviewError = err;
- this.openGraphService.fail('block-viz-' + this.rawId);
+ this.openGraphService.fail({ event: 'block-viz-' + this.rawId, sessionId: this.ogSession });
return of([]);
}),
switchMap((transactions) => {
@@ -138,7 +140,8 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
),
this.stateService.env.ACCELERATOR === true && block.height > 819500
? this.servicesApiService.getAllAccelerationHistory$({ blockHeight: block.height })
- .pipe(catchError(() => {
+ .pipe(
+ catchError(() => {
return of([]);
}))
: of([])
@@ -169,8 +172,8 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
this.error = error;
this.isLoadingOverview = false;
this.seoService.logSoft404();
- this.openGraphService.fail('block-viz-' + this.rawId);
- this.openGraphService.fail('block-data-' + this.rawId);
+ this.openGraphService.fail({ event: 'block-viz-' + this.rawId, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'block-data-' + this.rawId, sessionId: this.ogSession });
if (this.blockGraph) {
this.blockGraph.destroy();
}
@@ -196,6 +199,6 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
}
onGraphReady(): void {
- this.openGraphService.waitOver('block-viz-' + this.rawId);
+ this.openGraphService.waitOver({ event: 'block-viz-' + this.rawId, sessionId: this.ogSession });
}
}
diff --git a/frontend/src/app/components/pool/pool-preview.component.ts b/frontend/src/app/components/pool/pool-preview.component.ts
index 93077120d..7478e5f6f 100644
--- a/frontend/src/app/components/pool/pool-preview.component.ts
+++ b/frontend/src/app/components/pool/pool-preview.component.ts
@@ -30,6 +30,8 @@ export class PoolPreviewComponent implements OnInit {
slug: string = undefined;
+ ogSession: number;
+
constructor(
@Inject(LOCALE_ID) public locale: string,
private apiService: ApiService,
@@ -47,22 +49,22 @@ export class PoolPreviewComponent implements OnInit {
this.isLoading = true;
this.imageLoaded = false;
this.slug = slug;
- this.openGraphService.waitFor('pool-hash-' + this.slug);
- this.openGraphService.waitFor('pool-stats-' + this.slug);
- this.openGraphService.waitFor('pool-chart-' + this.slug);
- this.openGraphService.waitFor('pool-img-' + this.slug);
+ this.ogSession = this.openGraphService.waitFor('pool-hash-' + this.slug);
+ this.ogSession = this.openGraphService.waitFor('pool-stats-' + this.slug);
+ this.ogSession = this.openGraphService.waitFor('pool-chart-' + this.slug);
+ this.ogSession = this.openGraphService.waitFor('pool-img-' + this.slug);
return this.apiService.getPoolHashrate$(this.slug)
.pipe(
switchMap((data) => {
this.isLoading = false;
this.prepareChartOptions(data.map(val => [val.timestamp * 1000, val.avgHashrate]));
- this.openGraphService.waitOver('pool-hash-' + this.slug);
+ this.openGraphService.waitOver({ event: 'pool-hash-' + this.slug, sessionId: this.ogSession });
return [slug];
}),
catchError(() => {
this.isLoading = false;
this.seoService.logSoft404();
- this.openGraphService.fail('pool-hash-' + this.slug);
+ this.openGraphService.fail({ event: 'pool-hash-' + this.slug, sessionId: this.ogSession });
return of([slug]);
})
);
@@ -72,7 +74,7 @@ export class PoolPreviewComponent implements OnInit {
catchError(() => {
this.isLoading = false;
this.seoService.logSoft404();
- this.openGraphService.fail('pool-stats-' + this.slug);
+ this.openGraphService.fail({ event: 'pool-stats-' + this.slug, sessionId: this.ogSession });
return of(null);
})
);
@@ -90,11 +92,11 @@ export class PoolPreviewComponent implements OnInit {
}
poolStats.pool.regexes = regexes.slice(0, -3);
- this.openGraphService.waitOver('pool-stats-' + this.slug);
+ this.openGraphService.waitOver({ event: 'pool-stats-' + this.slug, sessionId: this.ogSession });
const logoSrc = `/resources/mining-pools/` + poolStats.pool.slug + '.svg';
if (logoSrc === this.lastImgSrc) {
- this.openGraphService.waitOver('pool-img-' + this.slug);
+ this.openGraphService.waitOver({ event: 'pool-img-' + this.slug, sessionId: this.ogSession });
}
this.lastImgSrc = logoSrc;
return Object.assign({
@@ -103,7 +105,7 @@ export class PoolPreviewComponent implements OnInit {
}),
catchError(() => {
this.isLoading = false;
- this.openGraphService.fail('pool-stats-' + this.slug);
+ this.openGraphService.fail({ event: 'pool-stats-' + this.slug, sessionId: this.ogSession });
return of(null);
})
);
@@ -170,16 +172,16 @@ export class PoolPreviewComponent implements OnInit {
}
onChartReady(): void {
- this.openGraphService.waitOver('pool-chart-' + this.slug);
+ this.openGraphService.waitOver({ event: 'pool-chart-' + this.slug, sessionId: this.ogSession });
}
onImageLoad(): void {
this.imageLoaded = true;
- this.openGraphService.waitOver('pool-img-' + this.slug);
+ this.openGraphService.waitOver({ event: 'pool-img-' + this.slug, sessionId: this.ogSession });
}
onImageFail(): void {
this.imageLoaded = false;
- this.openGraphService.waitOver('pool-img-' + this.slug);
+ this.openGraphService.waitOver({ event: 'pool-img-' + this.slug, sessionId: this.ogSession });
}
}
diff --git a/frontend/src/app/components/transaction/transaction-preview.component.ts b/frontend/src/app/components/transaction/transaction-preview.component.ts
index 0c51e0064..4746f9de7 100644
--- a/frontend/src/app/components/transaction/transaction-preview.component.ts
+++ b/frontend/src/app/components/transaction/transaction-preview.component.ts
@@ -43,6 +43,8 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
opReturns: Vout[];
extraData: 'none' | 'coinbase' | 'opreturn';
+ ogSession: number;
+
constructor(
private route: ActivatedRoute,
private electrsApiService: ElectrsApiService,
@@ -75,7 +77,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
)
.subscribe((cpfpInfo) => {
this.cpfpInfo = cpfpInfo;
- this.openGraphService.waitOver('cpfp-data-' + this.txId);
+ this.openGraphService.waitOver({ event: 'cpfp-data-' + this.txId, sessionId: this.ogSession });
});
this.subscription = this.route.paramMap
@@ -83,8 +85,8 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
switchMap((params: ParamMap) => {
const urlMatch = (params.get('id') || '').split(':');
this.txId = urlMatch[0];
- this.openGraphService.waitFor('tx-data-' + this.txId);
- this.openGraphService.waitFor('tx-time-' + this.txId);
+ this.ogSession = this.openGraphService.waitFor('tx-data-' + this.txId);
+ this.ogSession = this.openGraphService.waitFor('tx-time-' + this.txId);
this.seoService.setTitle(
$localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
);
@@ -138,7 +140,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
.subscribe((tx: Transaction) => {
if (!tx) {
this.seoService.logSoft404();
- this.openGraphService.fail('tx-data-' + this.txId);
+ this.openGraphService.fail({ event: 'tx-data-' + this.txId, sessionId: this.ogSession });
return;
}
@@ -155,10 +157,10 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
if (tx.status.confirmed) {
this.transactionTime = tx.status.block_time;
- this.openGraphService.waitOver('tx-time-' + this.txId);
+ this.openGraphService.waitOver({ event: 'tx-time-' + this.txId, sessionId: this.ogSession });
} else if (!tx.status.confirmed && tx.firstSeen) {
this.transactionTime = tx.firstSeen;
- this.openGraphService.waitOver('tx-time-' + this.txId);
+ this.openGraphService.waitOver({ event: 'tx-time-' + this.txId, sessionId: this.ogSession });
} else {
this.getTransactionTime();
}
@@ -184,11 +186,11 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
}
}
- this.openGraphService.waitOver('tx-data-' + this.txId);
+ this.openGraphService.waitOver({ event: 'tx-data-' + this.txId, sessionId: this.ogSession });
},
(error) => {
this.seoService.logSoft404();
- this.openGraphService.fail('tx-data-' + this.txId);
+ this.openGraphService.fail({ event: 'tx-data-' + this.txId, sessionId: this.ogSession });
this.error = error;
this.isLoadingTx = false;
}
@@ -205,7 +207,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
)
.subscribe((transactionTimes) => {
this.transactionTime = transactionTimes[0];
- this.openGraphService.waitOver('tx-time-' + this.txId);
+ this.openGraphService.waitOver({ event: 'tx-time-' + this.txId, sessionId: this.ogSession });
});
}
diff --git a/frontend/src/app/components/wallet/wallet-preview.component.ts b/frontend/src/app/components/wallet/wallet-preview.component.ts
index 0387822aa..bbf8ecf7a 100644
--- a/frontend/src/app/components/wallet/wallet-preview.component.ts
+++ b/frontend/src/app/components/wallet/wallet-preview.component.ts
@@ -125,6 +125,8 @@ export class WalletPreviewComponent implements OnInit, OnDestroy {
sent = 0;
chainBalance = 0;
+ ogSession: number;
+
constructor(
private route: ActivatedRoute,
private stateService: StateService,
@@ -141,9 +143,9 @@ export class WalletPreviewComponent implements OnInit, OnDestroy {
map((params: ParamMap) => params.get('wallet') as string),
tap((walletName: string) => {
this.walletName = walletName;
- this.openGraphService.waitFor('wallet-addresses-' + this.walletName);
- this.openGraphService.waitFor('wallet-data-' + this.walletName);
- this.openGraphService.waitFor('wallet-txs-' + this.walletName);
+ this.ogSession = this.openGraphService.waitFor('wallet-addresses-' + this.walletName);
+ this.ogSession = this.openGraphService.waitFor('wallet-data-' + this.walletName);
+ this.ogSession = this.openGraphService.waitFor('wallet-txs-' + this.walletName);
this.seoService.setTitle($localize`:@@wallet.component.browser-title:Wallet: ${walletName}:INTERPOLATION:`);
this.seoService.setDescription($localize`:@@meta.description.bitcoin.wallet:See mempool transactions, confirmed transactions, balance, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} wallet ${walletName}:INTERPOLATION:.`);
}),
@@ -152,9 +154,9 @@ export class WalletPreviewComponent implements OnInit, OnDestroy {
this.error = err;
this.seoService.logSoft404();
console.log(err);
- this.openGraphService.fail('wallet-addresses-' + this.walletName);
- this.openGraphService.fail('wallet-data-' + this.walletName);
- this.openGraphService.fail('wallet-txs-' + this.walletName);
+ this.openGraphService.fail({ event: 'wallet-addresses-' + this.walletName, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'wallet-data-' + this.walletName, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'wallet-txs-' + this.walletName, sessionId: this.ogSession });
return of({});
})
)),
@@ -185,13 +187,13 @@ export class WalletPreviewComponent implements OnInit, OnDestroy {
this.walletSubscription = this.walletAddresses$.subscribe(wallet => {
this.addressStrings = Object.keys(wallet);
this.addresses = Object.values(wallet);
- this.openGraphService.waitOver('wallet-addresses-' + this.walletName);
+ this.openGraphService.waitOver({ event: 'wallet-addresses-' + this.walletName, sessionId: this.ogSession });
});
this.walletSummary$ = this.wallet$.pipe(
map(wallet => this.deduplicateWalletTransactions(Object.values(wallet).flatMap(address => address.transactions))),
tap(() => {
- this.openGraphService.waitOver('wallet-txs-' + this.walletName);
+ this.openGraphService.waitOver({ event: 'wallet-txs-' + this.walletName, sessionId: this.ogSession });
})
);
@@ -209,7 +211,7 @@ export class WalletPreviewComponent implements OnInit, OnDestroy {
);
}),
tap(() => {
- this.openGraphService.waitOver('wallet-data-' + this.walletName);
+ this.openGraphService.waitOver({ event: 'wallet-data-' + this.walletName, sessionId: this.ogSession });
})
);
}
diff --git a/frontend/src/app/lightning/channel/channel-preview.component.ts b/frontend/src/app/lightning/channel/channel-preview.component.ts
index 84a85f9c6..2af0dcd57 100644
--- a/frontend/src/app/lightning/channel/channel-preview.component.ts
+++ b/frontend/src/app/lightning/channel/channel-preview.component.ts
@@ -18,6 +18,8 @@ export class ChannelPreviewComponent implements OnInit {
channelGeo: number[] = [];
shortId: string;
+ ogSession: number;
+
constructor(
private lightningApiService: LightningApiService,
private activatedRoute: ActivatedRoute,
@@ -30,8 +32,8 @@ export class ChannelPreviewComponent implements OnInit {
.pipe(
switchMap((params: ParamMap) => {
this.shortId = params.get('short_id') || '';
- this.openGraphService.waitFor('channel-map-' + this.shortId);
- this.openGraphService.waitFor('channel-data-' + this.shortId);
+ this.ogSession = this.openGraphService.waitFor('channel-map-' + this.shortId);
+ this.ogSession = this.openGraphService.waitFor('channel-data-' + this.shortId);
this.error = null;
this.seoService.setTitle(`Channel: ${params.get('short_id')}`);
this.seoService.setDescription($localize`:@@meta.description.lightning.channel:Overview for Lightning channel ${params.get('short_id')}. See channel capacity, the Lightning nodes involved, related on-chain transactions, and more.`);
@@ -51,13 +53,13 @@ export class ChannelPreviewComponent implements OnInit {
data.node_right.longitude, data.node_right.latitude,
];
}
- this.openGraphService.waitOver('channel-data-' + this.shortId);
+ this.openGraphService.waitOver({ event: 'channel-data-' + this.shortId, sessionId: this.ogSession });
}),
catchError((err) => {
this.error = err;
this.seoService.logSoft404();
- this.openGraphService.fail('channel-map-' + this.shortId);
- this.openGraphService.fail('channel-data-' + this.shortId);
+ this.openGraphService.fail({ event: 'channel-map-' + this.shortId, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'channel-data-' + this.shortId, sessionId: this.ogSession });
return of(null);
})
);
@@ -66,6 +68,6 @@ export class ChannelPreviewComponent implements OnInit {
}
onMapReady() {
- this.openGraphService.waitOver('channel-map-' + this.shortId);
+ this.openGraphService.waitOver({ event: 'channel-map-' + this.shortId, sessionId: this.ogSession });
}
}
diff --git a/frontend/src/app/lightning/group/group-preview.component.ts b/frontend/src/app/lightning/group/group-preview.component.ts
index 4b8f5ed77..4e7d56bbe 100644
--- a/frontend/src/app/lightning/group/group-preview.component.ts
+++ b/frontend/src/app/lightning/group/group-preview.component.ts
@@ -22,6 +22,8 @@ export class GroupPreviewComponent implements OnInit {
slug: string;
groupId: string;
+ ogSession: number;
+
constructor(
private lightningApiService: LightningApiService,
private activatedRoute: ActivatedRoute,
@@ -37,8 +39,8 @@ export class GroupPreviewComponent implements OnInit {
.pipe(
switchMap((params: ParamMap) => {
this.slug = params.get('slug');
- this.openGraphService.waitFor('ln-group-map-' + this.slug);
- this.openGraphService.waitFor('ln-group-data-' + this.slug);
+ this.ogSession = this.openGraphService.waitFor('ln-group-map-' + this.slug);
+ this.ogSession = this.openGraphService.waitFor('ln-group-data-' + this.slug);
if (this.slug === 'the-mempool-open-source-project') {
this.groupId = 'mempool.space';
@@ -52,8 +54,8 @@ export class GroupPreviewComponent implements OnInit {
description: '',
};
this.seoService.logSoft404();
- this.openGraphService.fail('ln-group-map-' + this.slug);
- this.openGraphService.fail('ln-group-data-' + this.slug);
+ this.openGraphService.fail({ event: 'ln-group-map-' + this.slug, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'ln-group-data-' + this.slug, sessionId: this.ogSession });
return of(null);
}
@@ -99,7 +101,7 @@ export class GroupPreviewComponent implements OnInit {
const sumLiquidity = nodes.reduce((partialSum, a) => partialSum + parseInt(a.capacity, 10), 0);
const sumChannels = nodes.reduce((partialSum, a) => partialSum + a.opened_channel_count, 0);
- this.openGraphService.waitOver('ln-group-data-' + this.slug);
+ this.openGraphService.waitOver({ event: 'ln-group-data-' + this.slug, sessionId: this.ogSession });
return {
nodes: nodes,
@@ -109,8 +111,8 @@ export class GroupPreviewComponent implements OnInit {
}),
catchError(() => {
this.seoService.logSoft404();
- this.openGraphService.fail('ln-group-map-' + this.slug);
- this.openGraphService.fail('ln-group-data-' + this.slug);
+ this.openGraphService.fail({ event: 'ln-group-map-' + this.slug, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'ln-group-data-' + this.slug, sessionId: this.ogSession });
return of({
nodes: [],
sumLiquidity: 0,
@@ -121,7 +123,7 @@ export class GroupPreviewComponent implements OnInit {
}
onMapReady(): void {
- this.openGraphService.waitOver('ln-group-map-' + this.slug);
+ this.openGraphService.waitOver({ event: 'ln-group-map-' + this.slug, sessionId: this.ogSession });
}
}
diff --git a/frontend/src/app/lightning/node/node-preview.component.ts b/frontend/src/app/lightning/node/node-preview.component.ts
index 259313de6..7a45ea905 100644
--- a/frontend/src/app/lightning/node/node-preview.component.ts
+++ b/frontend/src/app/lightning/node/node-preview.component.ts
@@ -27,6 +27,8 @@ export class NodePreviewComponent implements OnInit {
publicKeySize = 99;
+ ogSession: number;
+
constructor(
private lightningApiService: LightningApiService,
private activatedRoute: ActivatedRoute,
@@ -43,8 +45,8 @@ export class NodePreviewComponent implements OnInit {
.pipe(
switchMap((params: ParamMap) => {
this.publicKey = params.get('public_key');
- this.openGraphService.waitFor('node-map-' + this.publicKey);
- this.openGraphService.waitFor('node-data-' + this.publicKey);
+ this.ogSession = this.openGraphService.waitFor('node-map-' + this.publicKey);
+ this.ogSession = this.openGraphService.waitFor('node-data-' + this.publicKey);
return this.lightningApiService.getNode$(params.get('public_key'));
}),
map((node) => {
@@ -76,15 +78,15 @@ export class NodePreviewComponent implements OnInit {
this.socketTypes = Object.keys(socketTypesMap);
node.avgCapacity = node.capacity / Math.max(1, node.active_channel_count);
- this.openGraphService.waitOver('node-data-' + this.publicKey);
+ this.openGraphService.waitOver({ event: 'node-data-' + this.publicKey, sessionId: this.ogSession });
return node;
}),
catchError(err => {
this.error = err;
this.seoService.logSoft404();
- this.openGraphService.fail('node-map-' + this.publicKey);
- this.openGraphService.fail('node-data-' + this.publicKey);
+ this.openGraphService.fail({ event: 'node-map-' + this.publicKey, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'node-data-' + this.publicKey, sessionId: this.ogSession });
return [{
alias: this.publicKey,
public_key: this.publicKey,
@@ -102,6 +104,6 @@ export class NodePreviewComponent implements OnInit {
}
onMapReady() {
- this.openGraphService.waitOver('node-map-' + this.publicKey);
+ this.openGraphService.waitOver({ event: 'node-map-' + this.publicKey, sessionId: this.ogSession });
}
}
diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts
index 9fc071eb5..bab34ae8f 100644
--- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts
+++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts
@@ -19,6 +19,8 @@ export class NodesPerISPPreview implements OnInit {
id: string;
error: Error;
+ ogSession: number;
+
constructor(
private apiService: ApiService,
private seoService: SeoService,
@@ -32,8 +34,8 @@ export class NodesPerISPPreview implements OnInit {
switchMap((params: ParamMap) => {
this.id = params.get('isp');
this.isp = null;
- this.openGraphService.waitFor('isp-map-' + this.id);
- this.openGraphService.waitFor('isp-data-' + this.id);
+ this.ogSession = this.openGraphService.waitFor('isp-map-' + this.id);
+ this.ogSession = this.openGraphService.waitFor('isp-data-' + this.id);
return this.apiService.getNodeForISP$(params.get('isp'));
}),
map(response => {
@@ -75,7 +77,7 @@ export class NodesPerISPPreview implements OnInit {
}
topCountry.flag = getFlagEmoji(topCountry.iso);
- this.openGraphService.waitOver('isp-data-' + this.id);
+ this.openGraphService.waitOver({ event: 'isp-data-' + this.id, sessionId: this.ogSession });
return {
nodes: response.nodes,
@@ -87,8 +89,8 @@ export class NodesPerISPPreview implements OnInit {
catchError(err => {
this.error = err;
this.seoService.logSoft404();
- this.openGraphService.fail('isp-map-' + this.id);
- this.openGraphService.fail('isp-data-' + this.id);
+ this.openGraphService.fail({ event: 'isp-map-' + this.id, sessionId: this.ogSession });
+ this.openGraphService.fail({ event: 'isp-data-' + this.id, sessionId: this.ogSession });
return of({
nodes: [],
sumLiquidity: 0,
@@ -100,6 +102,6 @@ export class NodesPerISPPreview implements OnInit {
}
onMapReady() {
- this.openGraphService.waitOver('isp-map-' + this.id);
+ this.openGraphService.waitOver({ event: 'isp-map-' + this.id, sessionId: this.ogSession });
}
}
diff --git a/frontend/src/app/services/opengraph.service.ts b/frontend/src/app/services/opengraph.service.ts
index e969dd07a..47b9d87d4 100644
--- a/frontend/src/app/services/opengraph.service.ts
+++ b/frontend/src/app/services/opengraph.service.ts
@@ -12,8 +12,9 @@ import { LanguageService } from '@app/services/language.service';
export class OpenGraphService {
network = '';
defaultImageUrl = '';
- previewLoadingEvents = {};
- previewLoadingCount = 0;
+ previewLoadingEvents = {}; // pending count per event type
+ previewLoadingCount = 0; // number of unique events pending
+ sessionId = 1;
constructor(
private ngZone: NgZone,
@@ -45,7 +46,7 @@ export class OpenGraphService {
// expose routing method to global scope, so we can access it from the unfurler
window['ogService'] = {
- loadPage: (path) => { return this.loadPage(path) }
+ loadPage: (path) => { return this.loadPage(path); }
};
}
@@ -77,7 +78,7 @@ export class OpenGraphService {
}
/// register an event that needs to resolve before we can take a screenshot
- waitFor(event) {
+ waitFor(event: string): number {
if (!this.previewLoadingEvents[event]) {
this.previewLoadingEvents[event] = 1;
this.previewLoadingCount++;
@@ -85,24 +86,31 @@ export class OpenGraphService {
this.previewLoadingEvents[event]++;
}
this.metaService.updateTag({ property: 'og:preview:loading', content: 'loading'});
+ return this.sessionId;
}
// mark an event as resolved
// if all registered events have resolved, signal we are ready for a screenshot
- waitOver(event) {
+ waitOver({ event, sessionId }: { event: string, sessionId: number }) {
+ if (sessionId !== this.sessionId) {
+ return;
+ }
if (this.previewLoadingEvents[event]) {
this.previewLoadingEvents[event]--;
if (this.previewLoadingEvents[event] === 0 && this.previewLoadingCount > 0) {
- delete this.previewLoadingEvents[event]
+ delete this.previewLoadingEvents[event];
this.previewLoadingCount--;
}
- if (this.previewLoadingCount === 0) {
- this.metaService.updateTag({ property: 'og:preview:ready', content: 'ready'});
- }
+ }
+ if (this.previewLoadingCount === 0) {
+ this.metaService.updateTag({ property: 'og:preview:ready', content: 'ready'});
}
}
- fail(event) {
+ fail({ event, sessionId }: { event: string, sessionId: number }) {
+ if (sessionId !== this.sessionId) {
+ return;
+ }
if (this.previewLoadingEvents[event]) {
this.metaService.updateTag({ property: 'og:preview:fail', content: 'fail'});
}
@@ -111,6 +119,7 @@ export class OpenGraphService {
resetLoading() {
this.previewLoadingEvents = {};
this.previewLoadingCount = 0;
+ this.sessionId++;
this.metaService.removeTag("property='og:preview:loading'");
this.metaService.removeTag("property='og:preview:ready'");
this.metaService.removeTag("property='og:preview:fail'");
@@ -122,7 +131,7 @@ export class OpenGraphService {
this.resetLoading();
this.ngZone.run(() => {
this.router.navigateByUrl(path);
- })
+ });
}
}
}
From 831b923dda4244df789738471d64d05a1b377142 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Mon, 10 Feb 2025 15:32:16 +0100
Subject: [PATCH 063/534] Update transaction preview messages
---
.../components/transaction/transaction-raw.component.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html
index 450e18ecd..7f8723353 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.html
+++ b/frontend/src/app/components/transaction/transaction-raw.component.html
@@ -8,10 +8,10 @@
- Preview Transaction
+ Preview
- Fetch prevout data
+ Fetch missing prevouts
Error decoding transaction, reason: {{ error }}
@@ -44,9 +44,9 @@
@if (!hasPrevouts) {
@if (offlineMode) {
- Prevouts are not loaded, some fields like fee rate cannot be displayed.
+ Missing prevouts are not loaded. Some fields like fee rate cannot be calculated.
} @else {
- Error loading prevouts . {{ errorPrevouts ? 'Reason: ' + errorPrevouts : '' }}
+ Error loading missing prevouts . {{ errorPrevouts ? 'Reason: ' + errorPrevouts : '' }}
}
}
From 4c20d2b180152c69abf15a790d77fd20142e570e Mon Sep 17 00:00:00 2001
From: natsoni
Date: Mon, 10 Feb 2025 15:33:21 +0100
Subject: [PATCH 064/534] Move broadcast button to alert banner
---
.../transaction/transaction-raw.component.html | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html
index 7f8723353..b761bc8a9 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.html
+++ b/frontend/src/app/components/transaction/transaction-raw.component.html
@@ -30,7 +30,6 @@
- Broadcast
Broadcasted
✕
@@ -41,6 +40,16 @@
+
+
+
+
+ This transaction is stored locally in your browser. Broadcast it to add it to the mempool.
+
+
+ Broadcast
+
+
@if (!hasPrevouts) {
@if (offlineMode) {
From 80201c082127226226e955d56a236c615f3bef72 Mon Sep 17 00:00:00 2001
From: wiz
Date: Mon, 10 Feb 2025 09:33:07 -1000
Subject: [PATCH 065/534] ops: Add new Bitcoin nodes in SG1 and HNL to
bitcoin.conf
---
production/bitcoin.conf | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/production/bitcoin.conf b/production/bitcoin.conf
index 8fe17d921..adff1ef6b 100644
--- a/production/bitcoin.conf
+++ b/production/bitcoin.conf
@@ -12,6 +12,7 @@ rpcallowip=127.0.0.1
rpcuser=__BITCOIN_RPC_USER__
rpcpassword=__BITCOIN_RPC_PASS__
whitelist=127.0.0.1
+whitelist=209.146.50.0/23
whitelist=103.99.168.0/22
whitelist=2401:b140::/32
blocksxor=0
@@ -27,6 +28,10 @@ bind=0.0.0.0:8333
bind=[::]:8333
zmqpubrawblock=tcp://127.0.0.1:8334
zmqpubrawtx=tcp://127.0.0.1:8335
+#addnode=[2401:b140::92:201]:8333
+#addnode=[2401:b140::92:202]:8333
+#addnode=[2401:b140::92:203]:8333
+#addnode=[2401:b140::92:204]:8333
#addnode=[2401:b140:1::92:201]:8333
#addnode=[2401:b140:1::92:202]:8333
#addnode=[2401:b140:1::92:203]:8333
@@ -65,6 +70,10 @@ zmqpubrawtx=tcp://127.0.0.1:8335
#addnode=[2401:b140:4::92:210]:8333
#addnode=[2401:b140:4::92:211]:8333
#addnode=[2401:b140:4::92:212]:8333
+#addnode=[2401:b140:5::92:201]:8333
+#addnode=[2401:b140:5::92:202]:8333
+#addnode=[2401:b140:5::92:203]:8333
+#addnode=[2401:b140:5::92:204]:8333
[test]
daemon=1
@@ -74,6 +83,10 @@ bind=0.0.0.0:18333
bind=[::]:18333
zmqpubrawblock=tcp://127.0.0.1:18334
zmqpubrawtx=tcp://127.0.0.1:18335
+#addnode=[2401:b140::92:201]:18333
+#addnode=[2401:b140::92:202]:18333
+#addnode=[2401:b140::92:203]:18333
+#addnode=[2401:b140::92:204]:18333
#addnode=[2401:b140:1::92:201]:18333
#addnode=[2401:b140:1::92:202]:18333
#addnode=[2401:b140:1::92:203]:18333
@@ -112,6 +125,10 @@ zmqpubrawtx=tcp://127.0.0.1:18335
#addnode=[2401:b140:4::92:210]:18333
#addnode=[2401:b140:4::92:211]:18333
#addnode=[2401:b140:4::92:212]:18333
+#addnode=[2401:b140:5::92:201]:18333
+#addnode=[2401:b140:5::92:202]:18333
+#addnode=[2401:b140:5::92:203]:18333
+#addnode=[2401:b140:5::92:204]:18333
[signet]
daemon=1
@@ -121,6 +138,10 @@ bind=0.0.0.0:38333
bind=[::]:38333
zmqpubrawblock=tcp://127.0.0.1:38334
zmqpubrawtx=tcp://127.0.0.1:38335
+#addnode=[2401:b140::92:201]:38333
+#addnode=[2401:b140::92:202]:38333
+#addnode=[2401:b140::92:203]:38333
+#addnode=[2401:b140::92:204]:38333
#addnode=[2401:b140:1::92:201]:38333
#addnode=[2401:b140:1::92:202]:38333
#addnode=[2401:b140:1::92:203]:38333
@@ -161,6 +182,10 @@ zmqpubrawtx=tcp://127.0.0.1:38335
#addnode=[2401:b140:4::92:212]:38333
#addnode=[2401:b140:4::92:213]:38333
#addnode=[2401:b140:4::92:214]:38333
+#addnode=[2401:b140:5::92:201]:38333
+#addnode=[2401:b140:5::92:202]:38333
+#addnode=[2401:b140:5::92:203]:38333
+#addnode=[2401:b140:5::92:204]:38333
[testnet4]
daemon=1
@@ -170,6 +195,10 @@ 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::92:201]:48333
+#addnode=[2401:b140::92:202]:48333
+#addnode=[2401:b140::92:203]:48333
+#addnode=[2401:b140::92:204]:48333
#addnode=[2401:b140:1::92:201]:48333
#addnode=[2401:b140:1::92:202]:48333
#addnode=[2401:b140:1::92:203]:48333
@@ -210,3 +239,7 @@ zmqpubrawtx=tcp://127.0.0.1:48335
#addnode=[2401:b140:4::92:212]:48333
#addnode=[2401:b140:4::92:213]:48333
#addnode=[2401:b140:4::92:214]:48333
+#addnode=[2401:b140:5::92:201]:48333
+#addnode=[2401:b140:5::92:202]:48333
+#addnode=[2401:b140:5::92:203]:48333
+#addnode=[2401:b140:5::92:204]:48333
From c47af1f8b22921c9379a145768b7c5c9fc3427ab Mon Sep 17 00:00:00 2001
From: hunicus <93150691+hunicus@users.noreply.github.com>
Date: Tue, 11 Feb 2025 15:30:51 -0500
Subject: [PATCH 066/534] =?UTF-8?q?Add=20Mempool=C2=AE=20to=20trademark=20?=
=?UTF-8?q?guidelines?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../trademark-policy/trademark-policy.component.html | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.html b/frontend/src/app/components/trademark-policy/trademark-policy.component.html
index e12cbb8b2..82580be1c 100644
--- a/frontend/src/app/components/trademark-policy/trademark-policy.component.html
+++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.html
@@ -8,7 +8,7 @@
Trademark Policy and Guidelines
The Mempool Open Source Project ®
-
Updated: August 19, 2024
+
Updated: February 11, 2025
@@ -59,6 +59,7 @@
Mempool Accelerator
Mempool Enterprise
Mempool Liquidity
+
Mempool
mempool.space
Be your own explorer
Explore the full Bitcoin ecosystem
@@ -340,7 +341,7 @@
Also, if you are using our Marks in a way described in the sections "Uses for Which We Are Granting a License," you must include the following trademark attribution at the foot of the webpage where you have used the Mark (or, if in a book, on the credits page), on any packaging or labeling, and on advertising or marketing materials:
-
"The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."
+
"The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, Mempool®, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."
What to Do When You See Abuse
From d3b5c15f33fa4c54ec22bb5b9b27c6a2480ad78b Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 12 Feb 2025 15:03:52 +0000
Subject: [PATCH 067/534] [ops] check for pool updates every hour
---
production/mempool-config.mainnet.json | 1 +
production/mempool-config.signet.json | 1 +
production/mempool-config.testnet.json | 1 +
production/mempool-config.testnet4.json | 1 +
4 files changed, 4 insertions(+)
diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json
index b14e3cd07..9505601d2 100644
--- a/production/mempool-config.mainnet.json
+++ b/production/mempool-config.mainnet.json
@@ -14,6 +14,7 @@
"BLOCKS_SUMMARIES_INDEXING": true,
"GOGGLES_INDEXING": true,
"AUTOMATIC_POOLS_UPDATE": true,
+ "POOLS_UPDATE_DELAY": 3600,
"AUDIT": true,
"CPFP_INDEXING": true,
"RUST_GBT": true,
diff --git a/production/mempool-config.signet.json b/production/mempool-config.signet.json
index a0a2353cb..952845ae9 100644
--- a/production/mempool-config.signet.json
+++ b/production/mempool-config.signet.json
@@ -9,6 +9,7 @@
"API_URL_PREFIX": "/api/v1/",
"INDEXING_BLOCKS_AMOUNT": -1,
"AUTOMATIC_POOLS_UPDATE": true,
+ "POOLS_UPDATE_DELAY": 3600,
"AUDIT": true,
"RUST_GBT": true,
"POLL_RATE_MS": 1000,
diff --git a/production/mempool-config.testnet.json b/production/mempool-config.testnet.json
index 81cd61dc4..5f9f3abb9 100644
--- a/production/mempool-config.testnet.json
+++ b/production/mempool-config.testnet.json
@@ -9,6 +9,7 @@
"API_URL_PREFIX": "/api/v1/",
"INDEXING_BLOCKS_AMOUNT": -1,
"AUTOMATIC_POOLS_UPDATE": true,
+ "POOLS_UPDATE_DELAY": 3600,
"AUDIT": true,
"RUST_GBT": true,
"POLL_RATE_MS": 1000,
diff --git a/production/mempool-config.testnet4.json b/production/mempool-config.testnet4.json
index 91373d223..2e79309ed 100644
--- a/production/mempool-config.testnet4.json
+++ b/production/mempool-config.testnet4.json
@@ -9,6 +9,7 @@
"API_URL_PREFIX": "/api/v1/",
"INDEXING_BLOCKS_AMOUNT": -1,
"AUTOMATIC_POOLS_UPDATE": true,
+ "POOLS_UPDATE_DELAY": 3600,
"AUDIT": true,
"RUST_GBT": true,
"POLL_RATE_MS": 1000,
From c626bd1ea2b51d8eed9787e9f701613a5887279a Mon Sep 17 00:00:00 2001
From: wiz
Date: Wed, 19 Feb 2025 10:56:13 -0600
Subject: [PATCH 068/534] ops: Remove old X-Frame-Options HTTP header
---
production/nginx/server-common.conf | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/production/nginx/server-common.conf b/production/nginx/server-common.conf
index 9a2a582c0..5a0b17b4e 100644
--- a/production/nginx/server-common.conf
+++ b/production/nginx/server-common.conf
@@ -8,33 +8,28 @@ add_header Onion-Location http://$onion.onion$request_uri;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
# generate frame configuration from origin header
-if ($frameOptions = '')
+if ($contentSecurityPolicy = '')
{
- set $frameOptions "DENY";
- set $contentSecurityPolicy "frame-ancestors 'none'";
+ set $contentSecurityPolicy "frame-ancestors 'self'";
}
# used for iframes on https://mempool.space/network
if ($http_referer ~ ^https://mempool.space/)
{
- set $frameOptions "ALLOW-FROM https://mempool.space";
set $contentSecurityPolicy "frame-ancestors https://mempool.space";
}
# used for iframes on https://mempool.ninja/network
if ($http_referer ~ ^https://mempool.ninja/)
{
- set $frameOptions "ALLOW-FROM https://mempool.ninja";
set $contentSecurityPolicy "frame-ancestors https://mempool.ninja";
}
# used for iframes on https://wiz.biz/bitcoin/nodes
if ($http_referer ~ ^https://wiz.biz/)
{
- set $frameOptions "ALLOW-FROM https://wiz.biz";
set $contentSecurityPolicy "frame-ancestors https://wiz.biz";
}
# restrict usage of frames
-add_header X-Frame-Options $frameOptions;
add_header Content-Security-Policy $contentSecurityPolicy;
# enable browser and proxy caching
From 7671600455b94bcbc96907bb1bdf07bc39be094f Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 19 Feb 2025 15:03:03 +0000
Subject: [PATCH 069/534] temporary twidget mirror
---
.../twitter-widget.component.ts | 54 +++++++++++--------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/frontend/src/app/components/twitter-widget/twitter-widget.component.ts b/frontend/src/app/components/twitter-widget/twitter-widget.component.ts
index 06b50b1dc..8f5894ad0 100644
--- a/frontend/src/app/components/twitter-widget/twitter-widget.component.ts
+++ b/frontend/src/app/components/twitter-widget/twitter-widget.component.ts
@@ -34,29 +34,39 @@ export class TwitterWidgetComponent implements OnChanges {
}
setIframeSrc(): void {
- if (this.handle) {
- this.iframeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL,
- `https://syndication.x.com/srv/timeline-profile/screen-name/${this.handle}?creatorScreenName=mempool`
- + '&dnt=true'
- + '&embedId=twitter-widget-0'
- + '&features=eyJ0ZndfdGltZWxpbmVfgbGlzdCI6eyJidWNrZXQiOltdLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2ZvbGxvd2VyX2NvdW50X3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9iYWNrZW5kIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19yZWZzcmNfc2Vzc2lvbiI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfZm9zbnJfc29mdF9pbnRlcnZlbnRpb25zX2VuYWJsZWQiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X21peGVkX21lZGlhXzE1ODk3Ijp7ImJ1Y2tldCI6InRyZWF0bWVudCIsInZlcnNpb24iOm51bGx9LCJ0ZndfZXhwZXJpbWVudHNfY29va2llX2V4cGlyYXRpb24iOnsiYnVja2V0IjoxMjA5NjAwLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYmlyZHdhdGNoX3Bpdm90c19lbmFibGVkIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19kdXBsaWNhdGVfc2NyaWJlc190b19zZXR0aW5ncyI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdXNlX3Byb2ZpbGVfaW1hZ2Vfc2hhcGVfZW5hYmxlZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdmlkZW9faGxzX2R5bmFtaWNfbWFuaWZlc3RzXzE1MDgyIjp7ImJ1Y2tldCI6InRydWVfYml0cmF0ZSIsInZlcnNpb24iOm51bGx9LCJ0ZndfbGVnYWN5X3RpbWVsaW5lX3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9mcm9udGVuZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9fQ%3D%3D'
- + '&frame=false'
- + '&hideBorder=true'
- + '&hideFooter=false'
- + '&hideHeader=true'
- + '&hideScrollBar=false'
- + `&lang=${this.lang}`
- + '&maxHeight=500px'
- + '&origin=https%3A%2F%2Fmempool.space%2F'
- // + '&sessionId=88f6d661d0dcca99c43c0a590f6a3e61c89226a9'
- + '&showHeader=false'
- + '&showReplies=false'
- + '&siteScreenName=mempool'
- + '&theme=dark'
- + '&transparent=true'
- + '&widgetsVersion=2615f7e52b7e0%3A1702314776716'
- ));
+ if (!this.handle) {
+ return;
}
+ let url = `https://syndication.x.com/srv/timeline-profile/screen-name/${this.handle}?creatorScreenName=mempool`
+ + '&dnt=true'
+ + '&embedId=twitter-widget-0'
+ + '&features=eyJ0ZndfdGltZWxpbmVfgbGlzdCI6eyJidWNrZXQiOltdLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2ZvbGxvd2VyX2NvdW50X3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9iYWNrZW5kIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19yZWZzcmNfc2Vzc2lvbiI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfZm9zbnJfc29mdF9pbnRlcnZlbnRpb25zX2VuYWJsZWQiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X21peGVkX21lZGlhXzE1ODk3Ijp7ImJ1Y2tldCI6InRyZWF0bWVudCIsInZlcnNpb24iOm51bGx9LCJ0ZndfZXhwZXJpbWVudHNfY29va2llX2V4cGlyYXRpb24iOnsiYnVja2V0IjoxMjA5NjAwLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYmlyZHdhdGNoX3Bpdm90c19lbmFibGVkIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19kdXBsaWNhdGVfc2NyaWJlc190b19zZXR0aW5ncyI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdXNlX3Byb2ZpbGVfaW1hZ2Vfc2hhcGVfZW5hYmxlZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdmlkZW9faGxzX2R5bmFtaWNfbWFuaWZlc3RzXzE1MDgyIjp7ImJ1Y2tldCI6InRydWVfYml0cmF0ZSIsInZlcnNpb24iOm51bGx9LCJ0ZndfbGVnYWN5X3RpbWVsaW5lX3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9mcm9udGVuZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9fQ%3D%3D'
+ + '&frame=false'
+ + '&hideBorder=true'
+ + '&hideFooter=false'
+ + '&hideHeader=true'
+ + '&hideScrollBar=false'
+ + `&lang=${this.lang}`
+ + '&maxHeight=500px'
+ + '&origin=https%3A%2F%2Fmempool.space%2F'
+ // + '&sessionId=88f6d661d0dcca99c43c0a590f6a3e61c89226a9'
+ + '&showHeader=false'
+ + '&showReplies=false'
+ + '&siteScreenName=mempool'
+ + '&theme=dark'
+ + '&transparent=true'
+ + '&widgetsVersion=2615f7e52b7e0%3A1702314776716';
+ switch (this.handle.toLowerCase()) {
+ case 'nayibbukele':
+ url = 'https://bitcoin.gob.sv/twidget';
+ break;
+ case 'metaplanet_jp':
+ url = 'https://metaplanet.mempool.space/twidget';
+ break;
+ default:
+ break;
+ }
+ this.iframeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, url));
}
onReady(): void {
From 6ec1cc3fd5bea16f0ecdf45c76c667939b35abe0 Mon Sep 17 00:00:00 2001
From: softsimon
Date: Thu, 20 Feb 2025 22:08:14 +0700
Subject: [PATCH 070/534] Deprecating the tv view
---
frontend/cypress/e2e/liquid/liquid.spec.ts | 5 --
.../e2e/liquidtestnet/liquidtestnet.spec.ts | 5 --
frontend/cypress/e2e/mainnet/mainnet.spec.ts | 20 -----
.../liquid-master-page.component.html | 5 --
.../statistics/statistics.component.html | 3 -
.../television/television.component.html | 25 ------
.../television/television.component.scss | 80 -----------------
.../television/television.component.ts | 86 -------------------
frontend/src/app/graphs/graphs.module.ts | 2 -
.../src/app/graphs/graphs.routing.module.ts | 6 --
10 files changed, 237 deletions(-)
delete mode 100644 frontend/src/app/components/television/television.component.html
delete mode 100644 frontend/src/app/components/television/television.component.scss
delete mode 100644 frontend/src/app/components/television/television.component.ts
diff --git a/frontend/cypress/e2e/liquid/liquid.spec.ts b/frontend/cypress/e2e/liquid/liquid.spec.ts
index c7d2a92ee..4fb7431d9 100644
--- a/frontend/cypress/e2e/liquid/liquid.spec.ts
+++ b/frontend/cypress/e2e/liquid/liquid.spec.ts
@@ -57,11 +57,6 @@ describe('Liquid', () => {
});
});
- it('loads the tv page - desktop', () => {
- cy.visit(`${basePath}/tv`);
- cy.waitForSkeletonGone();
- });
-
it('loads the graphs page - mobile', () => {
cy.visit(`${basePath}`)
cy.waitForSkeletonGone();
diff --git a/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts b/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts
index 54e355ce8..7befda49f 100644
--- a/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts
+++ b/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts
@@ -57,11 +57,6 @@ describe('Liquid Testnet', () => {
cy.waitForSkeletonGone();
});
- it('loads the tv page - desktop', () => {
- cy.visit(`${basePath}/tv`);
- cy.waitForSkeletonGone();
- });
-
it('loads the graphs page - mobile', () => {
cy.visit(`${basePath}`)
cy.waitForSkeletonGone();
diff --git a/frontend/cypress/e2e/mainnet/mainnet.spec.ts b/frontend/cypress/e2e/mainnet/mainnet.spec.ts
index 7e17c09cd..08a4741b3 100644
--- a/frontend/cypress/e2e/mainnet/mainnet.spec.ts
+++ b/frontend/cypress/e2e/mainnet/mainnet.spec.ts
@@ -415,26 +415,6 @@ describe('Mainnet', () => {
});
});
- it('loads the tv screen - desktop', () => {
- cy.viewport('macbook-16');
- cy.visit('/graphs/mempool');
- cy.waitForSkeletonGone();
- cy.get('#btn-tv').click().then(() => {
- cy.viewport('macbook-16');
- cy.get('.chart-holder');
- cy.get('.blockchain-wrapper').should('be.visible');
- cy.get('#mempool-block-0').should('be.visible');
- });
- });
-
- it('loads the tv screen - mobile', () => {
- cy.viewport('iphone-6');
- cy.visit('/tv');
- cy.waitForSkeletonGone();
- cy.get('.chart-holder');
- cy.get('.blockchain-wrapper').should('not.visible');
- });
-
it('loads the api screen', () => {
cy.visit('/');
cy.waitForSkeletonGone();
diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html
index 7e39d9341..cd016471b 100644
--- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html
+++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html
@@ -70,11 +70,6 @@
-
diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html
index 168a3c0c3..eb37cc858 100644
--- a/frontend/src/app/components/statistics/statistics.component.html
+++ b/frontend/src/app/components/statistics/statistics.component.html
@@ -16,9 +16,6 @@
-
-
-
diff --git a/frontend/src/app/components/television/television.component.html b/frontend/src/app/components/television/television.component.html
deleted file mode 100644
index 23dd18389..000000000
--- a/frontend/src/app/components/television/television.component.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
diff --git a/frontend/src/app/components/television/television.component.scss b/frontend/src/app/components/television/television.component.scss
deleted file mode 100644
index 9a6cbcc24..000000000
--- a/frontend/src/app/components/television/television.component.scss
+++ /dev/null
@@ -1,80 +0,0 @@
-
-.loading {
- margin: auto;
- width: 100%;
- display: flex;
- text-align: center;
- justify-content: center;
- height: 100vh;
- align-items: center;
-}
-
-#tv-wrapper {
- height: 100vh;
- overflow: hidden;
- position: relative;
-}
-
-.chart-holder {
- position: relative;
- height: 655px;
- width: 100%;
- margin: 30px auto 0;
-}
-
-.blockchain-wrapper {
- display: block;
- height: 100%;
- min-height: 240px;
- position: relative;
- top: 30px;
-
- .position-container {
- position: absolute;
- left: 0;
- bottom: 170px;
- transform: translateX(50vw);
- }
-
- #divider {
- width: 2px;
- height: 175px;
- left: 0;
- top: -40px;
- position: absolute;
- img {
- position: absolute;
- left: -100px;
- top: -28px;
- }
- }
-
- &.time-ltr {
- .blocks-wrapper {
- transform: scaleX(-1);
- }
- }
-}
-
-:host-context(.ltr-layout) {
- .blockchain-wrapper.time-ltr .blocks-wrapper,
- .blockchain-wrapper .blocks-wrapper {
- direction: ltr;
- }
-}
-
-:host-context(.rtl-layout) {
- .blockchain-wrapper.time-ltr .blocks-wrapper,
- .blockchain-wrapper .blocks-wrapper {
- direction: rtl;
- }
-}
-
-.tv-container {
- display: flex;
- margin-top: 0px;
- flex-direction: column;
-}
-
-
-
diff --git a/frontend/src/app/components/television/television.component.ts b/frontend/src/app/components/television/television.component.ts
deleted file mode 100644
index 1507f3d97..000000000
--- a/frontend/src/app/components/television/television.component.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { Component, OnInit, OnDestroy } from '@angular/core';
-import { WebsocketService } from '@app/services/websocket.service';
-import { OptimizedMempoolStats } from '@interfaces/node-api.interface';
-import { StateService } from '@app/services/state.service';
-import { ApiService } from '@app/services/api.service';
-import { SeoService } from '@app/services/seo.service';
-import { ActivatedRoute } from '@angular/router';
-import { map, scan, startWith, switchMap, tap } from 'rxjs/operators';
-import { interval, merge, Observable, Subscription } from 'rxjs';
-import { ChangeDetectionStrategy } from '@angular/core';
-
-@Component({
- selector: 'app-television',
- templateUrl: './television.component.html',
- styleUrls: ['./television.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class TelevisionComponent implements OnInit, OnDestroy {
-
- mempoolStats: OptimizedMempoolStats[] = [];
- statsSubscription$: Observable
;
- fragment: string;
- timeLtrSubscription: Subscription;
- timeLtr: boolean = this.stateService.timeLtr.value;
-
- constructor(
- private websocketService: WebsocketService,
- private apiService: ApiService,
- private stateService: StateService,
- private seoService: SeoService,
- private route: ActivatedRoute
- ) { }
-
- refreshStats(time: number, fn: Observable) {
- return interval(time).pipe(startWith(0), switchMap(() => fn));
- }
-
- ngOnInit() {
- this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`);
- this.seoService.setDescription($localize`:@@meta.description.tv:See Bitcoin blocks and mempool congestion in real-time in a simplified format perfect for a TV.`);
- this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']);
-
- this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
- this.timeLtr = !!ltr;
- });
-
- this.statsSubscription$ = merge(
- this.stateService.live2Chart$.pipe(map(stats => [stats])),
- this.route.fragment
- .pipe(
- tap(fragment => { this.fragment = fragment ?? '2h'; }),
- switchMap((fragment) => {
- const minute = 60000; const hour = 3600000;
- switch (fragment) {
- case '24h': return this.apiService.list24HStatistics$();
- case '1w': return this.refreshStats(5 * minute, this.apiService.list1WStatistics$());
- case '1m': return this.refreshStats(30 * minute, this.apiService.list1MStatistics$());
- case '3m': return this.refreshStats(2 * hour, this.apiService.list3MStatistics$());
- case '6m': return this.refreshStats(3 * hour, this.apiService.list6MStatistics$());
- case '1y': return this.refreshStats(8 * hour, this.apiService.list1YStatistics$());
- case '2y': return this.refreshStats(8 * hour, this.apiService.list2YStatistics$());
- case '3y': return this.refreshStats(12 * hour, this.apiService.list3YStatistics$());
- default /* 2h */: return this.apiService.list2HStatistics$();
- }
- })
- )
- )
- .pipe(
- scan((mempoolStats, newStats) => {
- if (newStats.length > 1) {
- mempoolStats = newStats;
- } else if (['2h', '24h'].includes(this.fragment)) {
- mempoolStats.unshift(newStats[0]);
- const now = Date.now() / 1000;
- const start = now - (this.fragment === '2h' ? (2 * 60 * 60) : (24 * 60 * 60) );
- mempoolStats = mempoolStats.filter(p => p.added >= start);
- }
- return mempoolStats;
- })
- );
- }
-
- ngOnDestroy() {
- this.timeLtrSubscription.unsubscribe();
- }
-}
diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts
index f882b4221..8ebf06f7c 100644
--- a/frontend/src/app/graphs/graphs.module.ts
+++ b/frontend/src/app/graphs/graphs.module.ts
@@ -26,7 +26,6 @@ import { StatisticsComponent } from '@components/statistics/statistics.component
import { MempoolBlockComponent } from '@components/mempool-block/mempool-block.component';
import { PoolRankingComponent } from '@components/pool-ranking/pool-ranking.component';
import { PoolComponent } from '@components/pool/pool.component';
-import { TelevisionComponent } from '@components/television/television.component';
import { DashboardComponent } from '@app/dashboard/dashboard.component';
import { CustomDashboardComponent } from '@components/custom-dashboard/custom-dashboard.component';
import { MiningDashboardComponent } from '@components/mining-dashboard/mining-dashboard.component';
@@ -56,7 +55,6 @@ import { CommonModule } from '@angular/common';
AcceleratorDashboardComponent,
PoolComponent,
PoolRankingComponent,
- TelevisionComponent,
StatisticsComponent,
GraphsComponent,
diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts
index 886d55072..e8dbaece3 100644
--- a/frontend/src/app/graphs/graphs.routing.module.ts
+++ b/frontend/src/app/graphs/graphs.routing.module.ts
@@ -16,7 +16,6 @@ import { PoolRankingComponent } from '@components/pool-ranking/pool-ranking.comp
import { PoolComponent } from '@components/pool/pool.component';
import { StartComponent } from '@components/start/start.component';
import { StatisticsComponent } from '@components/statistics/statistics.component';
-import { TelevisionComponent } from '@components/television/television.component';
import { DashboardComponent } from '@app/dashboard/dashboard.component';
import { CustomDashboardComponent } from '@components/custom-dashboard/custom-dashboard.component';
import { AccelerationFeesGraphComponent } from '@components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component';
@@ -180,11 +179,6 @@ const routes: Routes = [
},
]
},
- {
- path: 'tv',
- data: { networks: ['bitcoin', 'liquid'] },
- component: TelevisionComponent
- },
];
@NgModule({
From e40ca40ecbe1061bb9749ee0536550bc811e9050 Mon Sep 17 00:00:00 2001
From: softsimon
Date: Thu, 20 Feb 2025 22:11:45 +0700
Subject: [PATCH 071/534] Remove tv icon dep
---
frontend/src/app/shared/shared.module.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts
index d937e6bbb..0870eeabf 100644
--- a/frontend/src/app/shared/shared.module.ts
+++ b/frontend/src/app/shared/shared.module.ts
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
- faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
+ faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft,
faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck,
faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline,
@@ -395,7 +395,6 @@ export class SharedModule {
constructor(library: FaIconLibrary) {
library.addIcons(faInfoCircle);
library.addIcons(faChartArea);
- library.addIcons(faTv);
library.addIcons(faClock);
library.addIcons(faTachometerAlt);
library.addIcons(faCubes);
From 650c3d949dd3e969fd7b1e9f08b129239c25b977 Mon Sep 17 00:00:00 2001
From: softsimon
Date: Sat, 22 Feb 2025 22:25:52 +0700
Subject: [PATCH 072/534] remove tv mode tests
---
frontend/cypress/e2e/signet/signet.spec.ts | 24 -------------------
.../cypress/e2e/testnet4/testnet4.spec.ts | 24 -------------------
2 files changed, 48 deletions(-)
diff --git a/frontend/cypress/e2e/signet/signet.spec.ts b/frontend/cypress/e2e/signet/signet.spec.ts
index 11c47d14d..ae591c6a7 100644
--- a/frontend/cypress/e2e/signet/signet.spec.ts
+++ b/frontend/cypress/e2e/signet/signet.spec.ts
@@ -60,30 +60,6 @@ describe('Signet', () => {
});
});
- describe.skip('tv mode', () => {
- it('loads the tv screen - desktop', () => {
- cy.viewport('macbook-16');
- cy.visit('/signet/graphs');
- cy.waitForSkeletonGone();
- cy.get('#btn-tv').click().then(() => {
- cy.get('.chart-holder').should('be.visible');
- cy.get('#mempool-block-0').should('be.visible');
- cy.get('.tv-only').should('not.exist');
- });
- });
-
- it('loads the tv screen - mobile', () => {
- cy.visit('/signet/graphs');
- cy.waitForSkeletonGone();
- cy.get('#btn-tv').click().then(() => {
- cy.viewport('iphone-8');
- cy.get('.chart-holder').should('be.visible');
- cy.get('.tv-only').should('not.exist');
- cy.get('#mempool-block-0').should('be.visible');
- });
- });
- });
-
it('loads the api screen', () => {
cy.visit('/signet');
cy.waitForSkeletonGone();
diff --git a/frontend/cypress/e2e/testnet4/testnet4.spec.ts b/frontend/cypress/e2e/testnet4/testnet4.spec.ts
index c67d2414b..97af0e08e 100644
--- a/frontend/cypress/e2e/testnet4/testnet4.spec.ts
+++ b/frontend/cypress/e2e/testnet4/testnet4.spec.ts
@@ -60,30 +60,6 @@ describe('Testnet4', () => {
});
});
- describe('tv mode', () => {
- it('loads the tv screen - desktop', () => {
- cy.viewport('macbook-16');
- cy.visit('/testnet4/graphs');
- cy.waitForSkeletonGone();
- cy.get('#btn-tv').click().then(() => {
- cy.wait(1000);
- cy.get('.tv-only').should('not.exist');
- cy.get('#mempool-block-0').should('be.visible');
- });
- });
-
- it('loads the tv screen - mobile', () => {
- cy.visit('/testnet4/graphs');
- cy.waitForSkeletonGone();
- cy.get('#btn-tv').click().then(() => {
- cy.viewport('iphone-6');
- cy.wait(1000);
- cy.get('.tv-only').should('not.exist');
- });
- });
- });
-
-
it('loads the api screen', () => {
cy.visit('/testnet4');
cy.waitForSkeletonGone();
From d82a9f6c6a58366d2f022499d6b82644278356cf Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Tue, 25 Feb 2025 18:56:29 -0800
Subject: [PATCH 073/534] Tweak Docker workflow
---
.github/workflows/on-tag.yml | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml
index 634a27ab9..ba9e1eb7b 100644
--- a/.github/workflows/on-tag.yml
+++ b/.github/workflows/on-tag.yml
@@ -2,7 +2,7 @@ name: Docker build on tag
env:
DOCKER_CLI_EXPERIMENTAL: enabled
TAG_FMT: "^refs/tags/(((.?[0-9]+){3,4}))$"
- DOCKER_BUILDKIT: 0
+ DOCKER_BUILDKIT: 1 # Enable BuildKit for better performance
COMPOSE_DOCKER_CLI_BUILD: 0
on:
@@ -25,13 +25,12 @@ jobs:
timeout-minutes: 120
name: Build and push to DockerHub
steps:
- # Workaround based on JonasAlfredsson/docker-on-tmpfs@v1.0.1
- name: Replace the current swap file
shell: bash
run: |
- sudo swapoff /mnt/swapfile
- sudo rm -v /mnt/swapfile
- sudo fallocate -l 13G /mnt/swapfile
+ sudo swapoff /mnt/swapfile || true
+ sudo rm -f /mnt/swapfile
+ sudo fallocate -l 16G /mnt/swapfile
sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile
@@ -50,7 +49,7 @@ jobs:
echo "Directory '/var/lib/docker' not found"
exit 1
fi
- sudo mount -t tmpfs -o size=10G tmpfs /var/lib/docker
+ sudo mount -t tmpfs -o size=12G tmpfs /var/lib/docker
sudo systemctl restart docker
sudo df -h | grep docker
@@ -75,10 +74,16 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
+ with:
+ platforms: linux/amd64,linux/arm64
id: qemu
- name: Setup Docker buildx action
uses: docker/setup-buildx-action@v3
+ with:
+ platforms: linux/amd64,linux/arm64
+ driver-opts: |
+ network=host
id: buildx
- name: Available platforms
@@ -89,19 +94,20 @@ jobs:
id: cache
with:
path: /tmp/.buildx-cache
- key: ${{ runner.os }}-buildx-${{ github.sha }}
+ key: ${{ runner.os }}-buildx-${{ matrix.service }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-buildx-
+ ${{ runner.os }}-buildx-${{ matrix.service }}-
- name: Run Docker buildx for ${{ matrix.service }} against tag
run: |
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
- --cache-to "type=local,dest=/tmp/.buildx-cache" \
+ --cache-to "type=local,dest=/tmp/.buildx-cache,mode=max" \
--platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--build-context rustgbt=./rust \
--build-context backend=./backend \
- --output "type=registry" ./${{ matrix.service }}/ \
- --build-arg commitHash=$SHORT_SHA
+ --output "type=registry,push=true" \
+ --build-arg commitHash=$SHORT_SHA \
+ ./${{ matrix.service }}/
\ No newline at end of file
From e6f13766d3c05a910d3d146f457762a30f6c86ee Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Tue, 25 Feb 2025 19:05:28 -0800
Subject: [PATCH 074/534] Update Docker images
---
docker/backend/Dockerfile | 29 ++++++++++++++++++-----------
docker/frontend/Dockerfile | 2 +-
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile
index 60d663f20..e56b07da3 100644
--- a/docker/backend/Dockerfile
+++ b/docker/backend/Dockerfile
@@ -1,20 +1,20 @@
-FROM node:20.15.0-buster-slim AS builder
+FROM rust:1.84-bookworm AS builder
ARG commitHash
ENV MEMPOOL_COMMIT_HASH=${commitHash}
WORKDIR /build
+
+RUN apt-get update && \
+ apt-get install -y curl ca-certificates && \
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
+ apt-get install -y nodejs build-essential python3 pkg-config && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
COPY . .
-RUN apt-get update
-RUN apt-get install -y build-essential python3 pkg-config curl ca-certificates
-
-# Install Rust via rustup
-RUN CPU_ARCH=$(uname -m); if [ "$CPU_ARCH" = "armv7l" ]; then c_rehash; fi
-#RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable
-#Workaround to run on github actions from https://github.com/rust-lang/rustup/issues/2700#issuecomment-1367488985
-RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sed 's#/proc/self/exe#\/bin\/sh#g' | sh -s -- -y --default-toolchain stable
-ENV PATH="/root/.cargo/bin:$PATH"
+ENV PATH="/usr/local/cargo/bin:$PATH"
COPY --from=backend . .
COPY --from=rustgbt . ../rust/
@@ -24,7 +24,14 @@ RUN npm install --omit=dev --omit=optional
WORKDIR /build
RUN npm run package
-FROM node:20.15.0-buster-slim
+FROM rust:1.84-bookworm AS runtime
+
+RUN apt-get update && \
+ apt-get install -y curl ca-certificates && \
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
+ apt-get install -y nodejs && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
WORKDIR /backend
diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile
index 8374ebe49..8d97c9dc6 100644
--- a/docker/frontend/Dockerfile
+++ b/docker/frontend/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:20.15.0-buster-slim AS builder
+FROM node:22-bookworm-slim AS builder
ARG commitHash
ENV DOCKER_COMMIT_HASH=${commitHash}
From cfe7c93755c38a0837cc4744add1c6a821fc2160 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Feb 2025 02:13:31 +0000
Subject: [PATCH 075/534] Bump axios from 1.7.2 to 1.8.1 in /backend
Bumps [axios](https://github.com/axios/axios) from 1.7.2 to 1.8.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.8.1)
---
updated-dependencies:
- dependency-name: axios
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
backend/package-lock.json | 14 +++++++-------
backend/package.json | 2 +-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 3f66fa25b..1aaa77f85 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -12,7 +12,7 @@
"dependencies": {
"@mempool/electrum-client": "1.1.9",
"@types/node": "^18.15.3",
- "axios": "1.7.2",
+ "axios": "1.8.1",
"bitcoinjs-lib": "~6.1.3",
"crypto-js": "~4.2.0",
"express": "~4.21.1",
@@ -2275,9 +2275,9 @@
}
},
"node_modules/axios": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
- "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
+ "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -9459,9 +9459,9 @@
"integrity": "sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ=="
},
"axios": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
- "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
+ "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
"requires": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
diff --git a/backend/package.json b/backend/package.json
index ee5944f93..efc5a4501 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -41,7 +41,7 @@
"dependencies": {
"@mempool/electrum-client": "1.1.9",
"@types/node": "^18.15.3",
- "axios": "1.7.2",
+ "axios": "1.8.1",
"bitcoinjs-lib": "~6.1.3",
"crypto-js": "~4.2.0",
"express": "~4.21.1",
From 5116da2626432b4c6a0818671cee19897370d5f6 Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Fri, 28 Feb 2025 23:20:40 -0800
Subject: [PATCH 076/534] Do not update the latest tag when building
---
.github/workflows/on-tag.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml
index ba9e1eb7b..8a846631c 100644
--- a/.github/workflows/on-tag.yml
+++ b/.github/workflows/on-tag.yml
@@ -105,7 +105,7 @@ jobs:
--cache-to "type=local,dest=/tmp/.buildx-cache,mode=max" \
--platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
- --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
+ # --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--build-context rustgbt=./rust \
--build-context backend=./backend \
--output "type=registry,push=true" \
From 9c358060aa0f881414a95ac9d52cf38d364845aa Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Fri, 28 Feb 2025 23:22:00 -0800
Subject: [PATCH 077/534] Add dispatch workflow to update the latest tag
---
.../workflows/docker_update_latest_tag.yml | 181 ++++++++++++++++++
1 file changed, 181 insertions(+)
create mode 100644 .github/workflows/docker_update_latest_tag.yml
diff --git a/.github/workflows/docker_update_latest_tag.yml b/.github/workflows/docker_update_latest_tag.yml
new file mode 100644
index 000000000..5d21697d5
--- /dev/null
+++ b/.github/workflows/docker_update_latest_tag.yml
@@ -0,0 +1,181 @@
+name: Docker - Update latest tag
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: 'The Docker image tag to pull'
+ required: true
+ type: string
+
+jobs:
+ retag-and-push:
+ strategy:
+ matrix:
+ service:
+ - frontend
+ - backend
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ id: buildx
+ with:
+ install: true
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+ with:
+ platforms: linux/amd64,linux/arm64
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKER_HUB_USER }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+
+ - name: Get source image manifest and SHAs
+ id: source-manifest
+ run: |
+ set -e
+ echo "Fetching source manifest..."
+ MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }})
+ if [ -z "$MANIFEST" ]; then
+ echo "No manifest found. Assuming single-arch image."
+ exit 1
+ fi
+
+ echo "Original source manifest:"
+ echo "$MANIFEST" | jq .
+
+ AMD64_SHA=$(echo "$MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
+ ARM64_SHA=$(echo "$MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="arm64" and .platform.os=="linux") | .digest')
+
+ if [ -z "$AMD64_SHA" ] || [ -z "$ARM64_SHA" ]; then
+ echo "Source image is not multi-arch (missing amd64 or arm64)"
+ exit 1
+ fi
+
+ echo "Source amd64 manifest digest: $AMD64_SHA"
+ echo "Source arm64 manifest digest: $ARM64_SHA"
+
+ echo "amd64_sha=$AMD64_SHA" >> $GITHUB_OUTPUT
+ echo "arm64_sha=$ARM64_SHA" >> $GITHUB_OUTPUT
+
+ - name: Pull and retag architecture-specific images
+ run: |
+ set -e
+
+ docker buildx inspect --bootstrap
+
+ # Remove any existing local images to avoid cache interference
+ echo "Removing existing local images if they exist..."
+ docker image rm ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }} || true
+
+ # Pull amd64 image by digest
+ echo "Pulling amd64 image by digest..."
+ docker pull --platform linux/amd64 ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }}
+ PULLED_AMD64_MANIFEST_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
+ PULLED_AMD64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} --format '{{.Id}}')
+ echo "Pulled amd64 manifest digest: $PULLED_AMD64_MANIFEST_DIGEST"
+ echo "Pulled amd64 image ID (sha256): $PULLED_AMD64_IMAGE_ID"
+
+ # Pull arm64 image by digest
+ echo "Pulling arm64 image by digest..."
+ docker pull --platform linux/arm64 ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }}
+ PULLED_ARM64_MANIFEST_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
+ PULLED_ARM64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} --format '{{.Id}}')
+ echo "Pulled arm64 manifest digest: $PULLED_ARM64_MANIFEST_DIGEST"
+ echo "Pulled arm64 image ID (sha256): $PULLED_ARM64_IMAGE_ID"
+
+ # Tag the images
+ docker tag ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64
+ docker tag ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64
+
+ # Verify tagged images
+ TAGGED_AMD64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 --format '{{.Id}}')
+ TAGGED_ARM64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 --format '{{.Id}}')
+ echo "Tagged amd64 image ID (sha256): $TAGGED_AMD64_IMAGE_ID"
+ echo "Tagged arm64 image ID (sha256): $TAGGED_ARM64_IMAGE_ID"
+
+ - name: Push architecture-specific images
+ run: |
+ set -e
+
+ echo "Pushing amd64 image..."
+ docker push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64
+ PUSHED_AMD64_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
+ echo "Pushed amd64 manifest digest (local): $PUSHED_AMD64_DIGEST"
+
+ # Fetch manifest from registry after push
+ echo "Fetching pushed amd64 manifest from registry..."
+ PUSHED_AMD64_REGISTRY_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64)
+ PUSHED_AMD64_REGISTRY_DIGEST=$(echo "$PUSHED_AMD64_REGISTRY_MANIFEST" | jq -r '.config.digest')
+ echo "Pushed amd64 manifest digest (registry): $PUSHED_AMD64_REGISTRY_DIGEST"
+
+ echo "Pushing arm64 image..."
+ docker push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64
+ PUSHED_ARM64_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
+ echo "Pushed arm64 manifest digest (local): $PUSHED_ARM64_DIGEST"
+
+ # Fetch manifest from registry after push
+ echo "Fetching pushed arm64 manifest from registry..."
+ PUSHED_ARM64_REGISTRY_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64)
+ PUSHED_ARM64_REGISTRY_DIGEST=$(echo "$PUSHED_ARM64_REGISTRY_MANIFEST" | jq -r '.config.digest')
+ echo "Pushed arm64 manifest digest (registry): $PUSHED_ARM64_REGISTRY_DIGEST"
+
+ - name: Create and push multi-arch manifest with original digests
+ run: |
+ set -e
+
+ echo "Creating multi-arch manifest with original digests..."
+ docker manifest create ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest \
+ ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} \
+ ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }}
+
+ echo "Pushing multi-arch manifest..."
+ docker manifest push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest
+
+ - name: Clean up intermediate tags
+ if: success()
+ run: |
+ docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 || true
+ docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 || true
+ docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }} || true
+
+ - name: Verify final manifest
+ run: |
+ set -e
+ echo "Fetching final generated manifest..."
+ FINAL_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest)
+ echo "Generated final manifest:"
+ echo "$FINAL_MANIFEST" | jq .
+
+ FINAL_AMD64_SHA=$(echo "$FINAL_MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
+ FINAL_ARM64_SHA=$(echo "$FINAL_MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="arm64" and .platform.os=="linux") | .digest')
+
+ echo "Final amd64 manifest digest: $FINAL_AMD64_SHA"
+ echo "Final arm64 manifest digest: $FINAL_ARM64_SHA"
+
+ # Compare all digests
+ echo "Comparing digests..."
+ echo "Source amd64 digest: ${{ steps.source-manifest.outputs.amd64_sha }}"
+ echo "Pulled amd64 manifest digest: $PULLED_AMD64_MANIFEST_DIGEST"
+ echo "Pushed amd64 manifest digest (local): $PUSHED_AMD64_DIGEST"
+ echo "Pushed amd64 manifest digest (registry): $PUSHED_AMD64_REGISTRY_DIGEST"
+ echo "Final amd64 digest: $FINAL_AMD64_SHA"
+ echo "Source arm64 digest: ${{ steps.source-manifest.outputs.arm64_sha }}"
+ echo "Pulled arm64 manifest digest: $PULLED_ARM64_MANIFEST_DIGEST"
+ echo "Pushed arm64 manifest digest (local): $PUSHED_ARM64_DIGEST"
+ echo "Pushed arm64 manifest digest (registry): $PUSHED_ARM64_REGISTRY_DIGEST"
+ echo "Final arm64 digest: $FINAL_ARM64_SHA"
+
+ if [ "$FINAL_AMD64_SHA" != "${{ steps.source-manifest.outputs.amd64_sha }}" ] || [ "$FINAL_ARM64_SHA" != "${{ steps.source-manifest.outputs.arm64_sha }}" ]; then
+ echo "Error: Final manifest SHAs do not match source SHAs"
+ exit 1
+ fi
+
+ echo "Successfully created multi-arch ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest from ${{ github.event.inputs.tag }}"
From c01e11899c78c14d3aa2a7c6371ed5015407f398 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 4 Mar 2025 16:25:54 +0100
Subject: [PATCH 078/534] PSBT support in transaction preview
---
.../transaction-raw.component.html | 4 +-
.../transaction/transaction-raw.component.ts | 92 +--
frontend/src/app/shared/transaction.utils.ts | 523 ++++++++++++++----
3 files changed, 458 insertions(+), 161 deletions(-)
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html
index b761bc8a9..3bd8ee6d2 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.html
+++ b/frontend/src/app/components/transaction/transaction-raw.component.html
@@ -6,7 +6,7 @@
Transaction hex
-
+
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.ts b/frontend/src/app/components/transaction/transaction-raw.component.ts
index 321b0ffe5..5ce170e12 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.ts
+++ b/frontend/src/app/components/transaction/transaction-raw.component.ts
@@ -22,6 +22,7 @@ import { CpfpInfo } from '../../interfaces/node-api.interface';
export class TransactionRawComponent implements OnInit, OnDestroy {
pushTxForm: UntypedFormGroup;
+ rawHexTransaction: string;
isLoading: boolean;
isLoadingPrevouts: boolean;
isLoadingCpfpInfo: boolean;
@@ -81,10 +82,10 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.resetState();
this.isLoading = true;
try {
- const tx = decodeRawTransaction(this.pushTxForm.get('txRaw').value, this.stateService.network);
+ const { tx, hex } = decodeRawTransaction(this.pushTxForm.get('txRaw').value, this.stateService.network);
await this.fetchPrevouts(tx);
await this.fetchCpfpInfo(tx);
- this.processTransaction(tx);
+ this.processTransaction(tx, hex);
} catch (error) {
this.error = error.message;
} finally {
@@ -93,57 +94,60 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
}
async fetchPrevouts(transaction: Transaction): Promise {
- if (this.offlineMode) {
- return;
- }
+ const prevoutsToFetch = transaction.vin.filter(input => !input.prevout).map((input) => ({ txid: input.txid, vout: input.vout }));
- const prevoutsToFetch = transaction.vin.map((input) => ({ txid: input.txid, vout: input.vout }));
+ if (!prevoutsToFetch.length || transaction.vin[0].is_coinbase || this.offlineMode) {
+ this.hasPrevouts = !prevoutsToFetch.length || transaction.vin[0].is_coinbase;
+ this.fetchCpfp = this.hasPrevouts && !this.offlineMode;
+ } else {
+ try {
+ this.missingPrevouts = [];
+ this.isLoadingPrevouts = true;
- if (!prevoutsToFetch.length || transaction.vin[0].is_coinbase) {
- this.hasPrevouts = true;
- return;
- }
+ const prevouts: { prevout: Vout, unconfirmed: boolean }[] = await firstValueFrom(this.apiService.getPrevouts$(prevoutsToFetch));
- try {
- this.missingPrevouts = [];
- this.isLoadingPrevouts = true;
-
- const prevouts: { prevout: Vout, unconfirmed: boolean }[] = await firstValueFrom(this.apiService.getPrevouts$(prevoutsToFetch));
-
- if (prevouts?.length !== prevoutsToFetch.length) {
- throw new Error();
- }
-
- transaction.vin = transaction.vin.map((input, index) => {
- if (prevouts[index]) {
- input.prevout = prevouts[index].prevout;
- addInnerScriptsToVin(input);
- } else {
- this.missingPrevouts.push(`${input.txid}:${input.vout}`);
+ if (prevouts?.length !== prevoutsToFetch.length) {
+ throw new Error();
}
- return input;
- });
- if (this.missingPrevouts.length) {
- throw new Error(`Some prevouts do not exist or are already spent (${this.missingPrevouts.length})`);
+ let fetchIndex = 0;
+ transaction.vin.forEach(input => {
+ if (!input.prevout) {
+ const fetched = prevouts[fetchIndex];
+ if (fetched) {
+ input.prevout = fetched.prevout;
+ } else {
+ this.missingPrevouts.push(`${input.txid}:${input.vout}`);
+ }
+ fetchIndex++;
+ }
+ });
+
+ if (this.missingPrevouts.length) {
+ throw new Error(`Some prevouts do not exist or are already spent (${this.missingPrevouts.length})`);
+ }
+
+ this.hasPrevouts = true;
+ this.isLoadingPrevouts = false;
+ this.fetchCpfp = prevouts.some(prevout => prevout?.unconfirmed);
+ } catch (error) {
+ console.log(error);
+ this.errorPrevouts = error?.error?.error || error?.message;
+ this.isLoadingPrevouts = false;
}
+ }
+ if (this.hasPrevouts) {
transaction.fee = transaction.vin.some(input => input.is_coinbase)
? 0
: transaction.vin.reduce((fee, input) => {
return fee + (input.prevout?.value || 0);
}, 0) - transaction.vout.reduce((sum, output) => sum + output.value, 0);
transaction.feePerVsize = transaction.fee / (transaction.weight / 4);
- transaction.sigops = countSigops(transaction);
-
- this.hasPrevouts = true;
- this.isLoadingPrevouts = false;
- this.fetchCpfp = prevouts.some(prevout => prevout?.unconfirmed);
- } catch (error) {
- console.log(error);
- this.errorPrevouts = error?.error?.error || error?.message;
- this.isLoadingPrevouts = false;
}
+
+ transaction.vin.forEach(addInnerScriptsToVin);
+ transaction.sigops = countSigops(transaction);
}
async fetchCpfpInfo(transaction: Transaction): Promise {
@@ -175,10 +179,11 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
}
}
- processTransaction(tx: Transaction): void {
+ processTransaction(tx: Transaction, hex: string): void {
this.transaction = tx;
+ this.rawHexTransaction = hex;
- this.transaction.flags = getTransactionFlags(this.transaction, null, null, null, this.stateService.network);
+ this.transaction.flags = getTransactionFlags(this.transaction, this.cpfpInfo, null, null, this.stateService.network);
this.filters = this.transaction.flags ? toFilters(this.transaction.flags).filter(f => f.txPage) : [];
if (this.transaction.sigops >= 0) {
this.adjustedVsize = Math.max(this.transaction.weight / 4, this.transaction.sigops * 5);
@@ -206,7 +211,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.isLoadingBroadcast = true;
this.errorBroadcast = null;
return new Promise((resolve, reject) => {
- this.apiService.postTransaction$(this.pushTxForm.get('txRaw').value)
+ this.apiService.postTransaction$(this.rawHexTransaction)
.subscribe((result) => {
this.isLoadingBroadcast = false;
this.successBroadcast = true;
@@ -228,6 +233,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
resetState() {
this.transaction = null;
+ this.rawHexTransaction = null;
this.error = null;
this.errorPrevouts = null;
this.errorBroadcast = null;
@@ -251,7 +257,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
resetForm() {
this.resetState();
- this.pushTxForm.reset();
+ this.pushTxForm.get('txRaw').setValue('');
}
@HostListener('window:resize', ['$event'])
diff --git a/frontend/src/app/shared/transaction.utils.ts b/frontend/src/app/shared/transaction.utils.ts
index b33d88c2f..eafe8ae99 100644
--- a/frontend/src/app/shared/transaction.utils.ts
+++ b/frontend/src/app/shared/transaction.utils.ts
@@ -692,7 +692,7 @@ export function addInnerScriptsToVin(vin: Vin): void {
if (vin.prevout.scriptpubkey_type === 'p2sh') {
const redeemScript = vin.scriptsig_asm.split(' ').reverse()[0];
vin.inner_redeemscript_asm = convertScriptSigAsm(redeemScript);
- if (vin.witness && vin.witness.length > 2) {
+ if (vin.witness && vin.witness.length) {
const witnessScript = vin.witness[vin.witness.length - 1];
vin.inner_witnessscript_asm = convertScriptSigAsm(witnessScript);
}
@@ -712,86 +712,15 @@ export function addInnerScriptsToVin(vin: Vin): void {
}
// Adapted from bitcoinjs-lib at https://github.com/bitcoinjs/bitcoinjs-lib/blob/32e08aa57f6a023e995d8c4f0c9fbdc5f11d1fa0/ts_src/transaction.ts#L78
-// Reads buffer of raw transaction data
-function fromBuffer(buffer: Uint8Array, network: string): Transaction {
+/**
+ * @param buffer The raw transaction data
+ * @param network
+ * @param inputs Additional information from a PSBT, if available
+ * @returns The decoded transaction object and the raw hex
+ */
+function fromBuffer(buffer: Uint8Array, network: string, inputs?: { key: Uint8Array; value: Uint8Array }[][]): { tx: Transaction, hex: string } {
let offset = 0;
- function readInt8(): number {
- if (offset + 1 > buffer.length) {
- throw new Error('Buffer out of bounds');
- }
- return buffer[offset++];
- }
-
- function readInt16() {
- if (offset + 2 > buffer.length) {
- throw new Error('Buffer out of bounds');
- }
- const value = buffer[offset] | (buffer[offset + 1] << 8);
- offset += 2;
- return value;
- }
-
- function readInt32(unsigned = false): number {
- if (offset + 4 > buffer.length) {
- throw new Error('Buffer out of bounds');
- }
- const value = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
- offset += 4;
- if (unsigned) {
- return value >>> 0;
- }
- return value;
- }
-
- function readInt64(): bigint {
- if (offset + 8 > buffer.length) {
- throw new Error('Buffer out of bounds');
- }
- const low = BigInt(buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24));
- const high = BigInt(buffer[offset + 4] | (buffer[offset + 5] << 8) | (buffer[offset + 6] << 16) | (buffer[offset + 7] << 24));
- offset += 8;
- return (high << 32n) | (low & 0xffffffffn);
- }
-
- function readVarInt(): bigint {
- const first = readInt8();
- if (first < 0xfd) {
- return BigInt(first);
- } else if (first === 0xfd) {
- return BigInt(readInt16());
- } else if (first === 0xfe) {
- return BigInt(readInt32(true));
- } else if (first === 0xff) {
- return readInt64();
- } else {
- throw new Error("Invalid VarInt prefix");
- }
- }
-
- function readSlice(n: number | bigint): Uint8Array {
- const length = Number(n);
- if (offset + length > buffer.length) {
- throw new Error('Cannot read slice out of bounds');
- }
- const slice = buffer.slice(offset, offset + length);
- offset += length;
- return slice;
- }
-
- function readVarSlice(): Uint8Array {
- return readSlice(readVarInt());
- }
-
- function readVector(): Uint8Array[] {
- const count = readVarInt();
- const vector = [];
- for (let i = 0; i < count; i++) {
- vector.push(readVarSlice());
- }
- return vector;
- }
-
// Parse raw transaction
const tx = {
status: {
@@ -802,39 +731,47 @@ function fromBuffer(buffer: Uint8Array, network: string): Transaction {
}
} as Transaction;
- tx.version = readInt32();
+ [tx.version, offset] = readInt32(buffer, offset);
- const marker = readInt8();
- const flag = readInt8();
+ let marker, flag;
+ [marker, offset] = readInt8(buffer, offset);
+ [flag, offset] = readInt8(buffer, offset);
- let hasWitnesses = false;
- if (
- marker === 0x00 &&
- flag === 0x01
- ) {
- hasWitnesses = true;
+ let isLegacyTransaction = true;
+ if (marker === 0x00 && flag === 0x01) {
+ isLegacyTransaction = false;
} else {
offset -= 2;
}
- const vinLen = readVarInt();
+ let vinLen;
+ [vinLen, offset] = readVarInt(buffer, offset);
+ if (vinLen === 0) {
+ throw new Error('Transaction has no inputs');
+ }
tx.vin = [];
for (let i = 0; i < vinLen; ++i) {
- const txid = uint8ArrayToHexString(readSlice(32).reverse());
- const vout = readInt32(true);
- const scriptsig = uint8ArrayToHexString(readVarSlice());
- const sequence = readInt32(true);
+ let txid, vout, scriptsig, sequence;
+ [txid, offset] = readSlice(buffer, offset, 32);
+ txid = uint8ArrayToHexString(txid.reverse());
+ [vout, offset] = readInt32(buffer, offset, true);
+ [scriptsig, offset] = readVarSlice(buffer, offset);
+ scriptsig = uint8ArrayToHexString(scriptsig);
+ [sequence, offset] = readInt32(buffer, offset, true);
const is_coinbase = txid === '0'.repeat(64);
const scriptsig_asm = convertScriptSigAsm(scriptsig);
tx.vin.push({ txid, vout, scriptsig, sequence, is_coinbase, scriptsig_asm, prevout: null });
}
- const voutLen = readVarInt();
+ let voutLen;
+ [voutLen, offset] = readVarInt(buffer, offset);
tx.vout = [];
for (let i = 0; i < voutLen; ++i) {
- const value = Number(readInt64());
- const scriptpubkeyArray = readVarSlice();
- const scriptpubkey = uint8ArrayToHexString(scriptpubkeyArray)
+ let value, scriptpubkeyArray, scriptpubkey;
+ [value, offset] = readInt64(buffer, offset);
+ value = Number(value);
+ [scriptpubkeyArray, offset] = readVarSlice(buffer, offset);
+ scriptpubkey = uint8ArrayToHexString(scriptpubkeyArray);
const scriptpubkey_asm = convertScriptSigAsm(scriptpubkey);
const toAddress = scriptPubKeyToAddress(scriptpubkey, network);
const scriptpubkey_type = toAddress.type;
@@ -842,48 +779,303 @@ function fromBuffer(buffer: Uint8Array, network: string): Transaction {
tx.vout.push({ value, scriptpubkey, scriptpubkey_asm, scriptpubkey_type, scriptpubkey_address });
}
- let witnessSize = 0;
- if (hasWitnesses) {
- const startOffset = offset;
+ if (!isLegacyTransaction) {
for (let i = 0; i < vinLen; ++i) {
- tx.vin[i].witness = readVector().map(uint8ArrayToHexString);
+ let witness;
+ [witness, offset] = readVector(buffer, offset);
+ tx.vin[i].witness = witness.map(uint8ArrayToHexString);
}
- witnessSize = offset - startOffset + 2;
}
- tx.locktime = readInt32(true);
+ [tx.locktime, offset] = readInt32(buffer, offset, true);
if (offset !== buffer.length) {
throw new Error('Transaction has unexpected data');
}
- tx.size = buffer.length;
- tx.weight = (tx.size - witnessSize) * 3 + tx.size;
+ // Optionally add data from PSBT: prevouts, redeem/witness scripts and signatures
+ if (inputs) {
+ for (let i = 0; i < tx.vin.length; i++) {
+ const vin = tx.vin[i];
+ const inputRecords = inputs[i];
- tx.txid = txid(tx);
+ const groups = {
+ nonWitnessUtxo: null,
+ witnessUtxo: null,
+ finalScriptSig: null,
+ finalScriptWitness: null,
+ redeemScript: null,
+ witnessScript: null,
+ partialSigs: []
+ };
- return tx;
-}
+ for (const record of inputRecords) {
+ switch (record.key[0]) {
+ case 0x00:
+ groups.nonWitnessUtxo = record;
+ break;
+ case 0x01:
+ groups.witnessUtxo = record;
+ break;
+ case 0x07:
+ groups.finalScriptSig = record;
+ break;
+ case 0x08:
+ groups.finalScriptWitness = record;
+ break;
+ case 0x04:
+ groups.redeemScript = record;
+ break;
+ case 0x05:
+ groups.witnessScript = record;
+ break;
+ case 0x02:
+ groups.partialSigs.push(record);
+ break;
+ }
+ }
-export function decodeRawTransaction(rawtx: string, network: string): Transaction {
- if (!rawtx.length || rawtx.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(rawtx)) {
- throw new Error('Invalid hex string');
+ // Fill prevout
+ if (groups.witnessUtxo && !vin.prevout) {
+ let value, scriptpubkeyArray, scriptpubkey, outputOffset = 0;
+ [value, outputOffset] = readInt64(groups.witnessUtxo.value, outputOffset);
+ value = Number(value);
+ [scriptpubkeyArray, outputOffset] = readVarSlice(groups.witnessUtxo.value, outputOffset);
+ scriptpubkey = uint8ArrayToHexString(scriptpubkeyArray);
+ const scriptpubkey_asm = convertScriptSigAsm(scriptpubkey);
+ const toAddress = scriptPubKeyToAddress(scriptpubkey, network);
+ const scriptpubkey_type = toAddress.type;
+ const scriptpubkey_address = toAddress?.address;
+ vin.prevout = { value, scriptpubkey, scriptpubkey_asm, scriptpubkey_type, scriptpubkey_address };
+ }
+ if (groups.nonWitnessUtxo && !vin.prevout) {
+ const utxoTx = fromBuffer(groups.nonWitnessUtxo.value, network).tx;
+ vin.prevout = utxoTx.vout[vin.vout];
+ }
+
+ // Fill final scriptSig or witness
+ let finalizedScriptSig = false;
+ if (groups.finalScriptSig) {
+ vin.scriptsig = uint8ArrayToHexString(groups.finalScriptSig.value);
+ vin.scriptsig_asm = convertScriptSigAsm(vin.scriptsig);
+ finalizedScriptSig = true;
+ }
+ let finalizedWitness = false;
+ if (groups.finalScriptWitness) {
+ let witness = [];
+ let witnessOffset = 0;
+ [witness, witnessOffset] = readVector(groups.finalScriptWitness.value, witnessOffset);
+ vin.witness = witness.map(uint8ArrayToHexString);
+ finalizedWitness = true;
+ }
+ if (finalizedScriptSig && finalizedWitness) {
+ continue;
+ }
+
+ // Fill redeem script and/or witness script
+ if (groups.redeemScript && !finalizedScriptSig) {
+ const redeemScript = groups.redeemScript.value;
+ if (redeemScript.length > 520) {
+ throw new Error("Redeem script must be <= 520 bytes");
+ }
+ let pushOpcode;
+ if (redeemScript.length < 0x4c) {
+ pushOpcode = new Uint8Array([redeemScript.length]);
+ } else if (redeemScript.length <= 0xff) {
+ pushOpcode = new Uint8Array([0x4c, redeemScript.length]); // OP_PUSHDATA1
+ } else {
+ pushOpcode = new Uint8Array([0x4d, redeemScript.length & 0xff, redeemScript.length >> 8]); // OP_PUSHDATA2
+ }
+ vin.scriptsig = (vin.scriptsig || '') + uint8ArrayToHexString(pushOpcode) + uint8ArrayToHexString(redeemScript);
+ vin.scriptsig_asm = convertScriptSigAsm(vin.scriptsig);
+ }
+ if (groups.witnessScript && !finalizedWitness) {
+ vin.witness = (vin.witness || []).concat(uint8ArrayToHexString(groups.witnessScript.value));
+ }
+
+
+ // Fill partial signatures
+ for (const record of groups.partialSigs) {
+ const scriptpubkey_type = vin.prevout?.scriptpubkey_type;
+ if (scriptpubkey_type === 'v0_p2wsh' && !finalizedWitness) {
+ vin.witness = vin.witness || [];
+ vin.witness.unshift(uint8ArrayToHexString(record.value));
+ }
+ if (scriptpubkey_type === 'p2sh') {
+ const redeemScriptStr = vin.scriptsig_asm ? vin.scriptsig_asm.split(' ').reverse()[0] : '';
+ if (redeemScriptStr.startsWith('00') && redeemScriptStr.length === 68 && vin.witness?.length) {
+ if (!finalizedWitness) {
+ vin.witness.unshift(uint8ArrayToHexString(record.value));
+ }
+ } else {
+ if (!finalizedScriptSig) {
+ const signature = record.value;
+ if (signature.length > 73) {
+ throw new Error("Signature must be <= 73 bytes");
+ }
+ const pushOpcode = new Uint8Array([signature.length]);
+ vin.scriptsig = uint8ArrayToHexString(pushOpcode) + uint8ArrayToHexString(signature) + (vin.scriptsig || '');
+ vin.scriptsig_asm = convertScriptSigAsm(vin.scriptsig);
+ }
+ }
+ }
+ }
+ }
}
- const buffer = new Uint8Array(rawtx.length / 2);
- for (let i = 0; i < rawtx.length; i += 2) {
- buffer[i / 2] = parseInt(rawtx.substring(i, i + 2), 16);
+ // Calculate final size, weight, and txid
+ const hasWitness = tx.vin.some(vin => vin.witness?.length);
+ let witnessSize = 0;
+ if (hasWitness) {
+ for (let i = 0; i < tx.vin.length; ++i) {
+ const witnessItems = tx.vin[i].witness || [];
+ witnessSize += getVarIntLength(witnessItems.length);
+ for (const item of witnessItems) {
+ const witnessItem = hexStringToUint8Array(item);
+ witnessSize += getVarIntLength(witnessItem.length);
+ witnessSize += witnessItem.length;
+ }
+ }
+ witnessSize += 2;
+ }
+
+ const rawHex = serializeTransaction(tx, hasWitness);
+ tx.size = rawHex.length;
+ tx.weight = (tx.size - witnessSize) * 3 + tx.size;
+ tx.txid = txid(tx);
+
+ return { tx, hex: uint8ArrayToHexString(rawHex) };
+}
+
+/**
+ * Decodes a PSBT buffer into the unsigned raw transaction and input map
+ * @param psbtBuffer
+ * @returns
+ * - the unsigned transaction from a PSBT (txHex)
+ * - the full input map for each input in to fill signatures and prevouts later (inputs)
+ */
+function decodePsbt(psbtBuffer: Uint8Array): { rawTx: Uint8Array; inputs: { key: Uint8Array; value: Uint8Array }[][] } {
+ let offset = 0;
+
+ // magic: "psbt" in ASCII
+ const expectedMagic = [0x70, 0x73, 0x62, 0x74];
+ for (let i = 0; i < expectedMagic.length; i++) {
+ if (psbtBuffer[offset + i] !== expectedMagic[i]) {
+ throw new Error("Invalid PSBT magic bytes");
+ }
+ }
+ offset += expectedMagic.length;
+
+ const separator = psbtBuffer[offset];
+ offset += 1;
+ if (separator !== 0xff) {
+ throw new Error("Invalid PSBT separator");
+ }
+
+ // GLOBAL MAP
+ let rawTx: Uint8Array | null = null;
+ while (offset < psbtBuffer.length) {
+ const [keyLen, newOffset] = readVarInt(psbtBuffer, offset);
+ offset = newOffset;
+ // key length of 0 means the end of the global map
+ if (keyLen === 0) {
+ break;
+ }
+ const key = psbtBuffer.slice(offset, offset + keyLen);
+ offset += keyLen;
+ const [valLen, newOffset2] = readVarInt(psbtBuffer, offset);
+ offset = newOffset2;
+ const value = psbtBuffer.slice(offset, offset + valLen);
+ offset += valLen;
+
+ // Global key type 0x00 holds the unsigned transaction.
+ if (key[0] === 0x00) {
+ rawTx = value;
+ }
+ }
+
+ if (!rawTx) {
+ throw new Error("Unsigned transaction not found in PSBT");
+ }
+
+ let numInputs: number;
+ let txOffset = 0;
+ // Skip version (4 bytes)
+ txOffset += 4;
+ if (rawTx[txOffset] === 0x00 && rawTx[txOffset + 1] === 0x01) {
+ txOffset += 2;
+ }
+ const [inputCount, newTxOffset] = readVarInt(rawTx, txOffset);
+ txOffset = newTxOffset;
+ numInputs = inputCount;
+
+ // INPUT MAPS
+ const inputs: { key: Uint8Array; value: Uint8Array }[][] = [];
+ for (let i = 0; i < numInputs; i++) {
+ const inputRecords: { key: Uint8Array; value: Uint8Array }[] = [];
+ const seenKeys = new Set();
+ while (offset < psbtBuffer.length) {
+ const [keyLen, newOffset] = readVarInt(psbtBuffer, offset);
+ offset = newOffset;
+ // key length of 0 means the end of the input map
+ if (keyLen === 0) {
+ break;
+ }
+ const key = psbtBuffer.slice(offset, offset + keyLen);
+ offset += keyLen;
+
+ const keyHex = uint8ArrayToHexString(key);
+ if (seenKeys.has(keyHex)) {
+ throw new Error(`Duplicate key in input map`);
+ }
+ seenKeys.add(keyHex);
+
+ const [valLen, newOffset2] = readVarInt(psbtBuffer, offset);
+ offset = newOffset2;
+ const value = psbtBuffer.slice(offset, offset + valLen);
+ offset += valLen;
+
+ inputRecords.push({ key, value });
+ }
+ inputs.push(inputRecords);
+ }
+
+ return { rawTx, inputs };
+}
+
+export function decodeRawTransaction(input: string, network: string): { tx: Transaction, hex: string } {
+ if (!input.length) {
+ throw new Error('Empty input');
+ }
+
+ let buffer: Uint8Array;
+ if (input.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(input)) {
+ buffer = hexStringToUint8Array(input);
+ } else if (/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}(?:==)|[A-Za-z0-9+/]{3}=)?$/.test(input)) {
+ buffer = base64ToUint8Array(input);
+ } else {
+ throw new Error('Invalid input: not a valid transaction or PSBT');
+ }
+
+ if (buffer[0] === 0x70 && buffer[1] === 0x73 && buffer[2] === 0x62 && buffer[3] === 0x74) { // PSBT magic bytes
+ const { rawTx, inputs } = decodePsbt(buffer);
+ return fromBuffer(rawTx, network, inputs);
}
return fromBuffer(buffer, network);
}
-function serializeTransaction(tx: Transaction): Uint8Array {
+function serializeTransaction(tx: Transaction, includeWitness: boolean = true): Uint8Array {
const result: number[] = [];
// Add version
result.push(...intToBytes(tx.version, 4));
+ if (includeWitness) {
+ // Add SegWit marker and flag bytes (0x00, 0x01)
+ result.push(0x00, 0x01);
+ }
+
// Add input count and inputs
result.push(...varIntToBytes(tx.vin.length));
for (const input of tx.vin) {
@@ -904,6 +1096,18 @@ function serializeTransaction(tx: Transaction): Uint8Array {
result.push(...scriptPubKey);
}
+ if (includeWitness) {
+ for (const input of tx.vin) {
+ const witnessItems = input.witness || [];
+ result.push(...varIntToBytes(witnessItems.length));
+ for (const item of witnessItems) {
+ const witnessBytes = hexStringToUint8Array(item);
+ result.push(...varIntToBytes(witnessBytes.length));
+ result.push(...witnessBytes);
+ }
+ }
+ }
+
// Add locktime
result.push(...intToBytes(tx.locktime, 4));
@@ -911,7 +1115,7 @@ function serializeTransaction(tx: Transaction): Uint8Array {
}
function txid(tx: Transaction): string {
- const serializedTx = serializeTransaction(tx);
+ const serializedTx = serializeTransaction(tx, false);
const hash1 = new Hash().update(serializedTx).digest();
const hash2 = new Hash().update(hash1).digest();
return uint8ArrayToHexString(hash2.reverse());
@@ -1188,6 +1392,11 @@ function hexStringToUint8Array(hex: string): Uint8Array {
return buf;
}
+function base64ToUint8Array(base64: string): Uint8Array {
+ const binaryString = atob(base64);
+ return new Uint8Array([...binaryString].map(char => char.charCodeAt(0)));
+}
+
function intToBytes(value: number, byteLength: number): number[] {
const bytes = [];
for (let i = 0; i < byteLength; i++) {
@@ -1230,6 +1439,88 @@ function varIntToBytes(value: number | bigint): number[] {
return bytes;
}
+function readInt8(buffer: Uint8Array, offset: number): [number, number] {
+ if (offset + 1 > buffer.length) {
+ throw new Error('Buffer out of bounds');
+ }
+ return [buffer[offset], offset + 1];
+}
+
+function readInt16(buffer: Uint8Array, offset: number): [number, number] {
+ if (offset + 2 > buffer.length) {
+ throw new Error('Buffer out of bounds');
+ }
+ return [buffer[offset] | (buffer[offset + 1] << 8), offset + 2];
+}
+
+function readInt32(buffer: Uint8Array, offset: number, unsigned: boolean = false): [number, number] {
+ if (offset + 4 > buffer.length) {
+ throw new Error('Buffer out of bounds');
+ }
+ const value = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
+ return [unsigned ? value >>> 0 : value, offset + 4];
+}
+
+function readInt64(buffer: Uint8Array, offset: number): [bigint, number] {
+ if (offset + 8 > buffer.length) {
+ throw new Error('Buffer out of bounds');
+ }
+ const low = BigInt(buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24));
+ const high = BigInt(buffer[offset + 4] | (buffer[offset + 5] << 8) | (buffer[offset + 6] << 16) | (buffer[offset + 7] << 24));
+ return [(high << 32n) | (low & 0xffffffffn), offset + 8];
+}
+
+function readVarInt(buffer: Uint8Array, offset: number): [number, number] {
+ const [first, newOffset] = readInt8(buffer, offset);
+
+ if (first < 0xfd) {
+ return [first, newOffset];
+ } else if (first === 0xfd) {
+ return readInt16(buffer, newOffset);
+ } else if (first === 0xfe) {
+ return readInt32(buffer, newOffset, true);
+ } else if (first === 0xff) {
+ const [bigValue, nextOffset] = readInt64(buffer, newOffset);
+
+ if (bigValue > Number.MAX_SAFE_INTEGER) {
+ throw new Error("VarInt exceeds safe integer range");
+ }
+
+ const numValue = Number(bigValue);
+ return [numValue, nextOffset];
+ } else {
+ throw new Error("Invalid VarInt prefix");
+ }
+}
+
+function readSlice(buffer: Uint8Array, offset: number, n: number | bigint): [Uint8Array, number] {
+ const length = Number(n);
+ if (offset + length > buffer.length) {
+ throw new Error('Cannot read slice out of bounds');
+ }
+ const slice = buffer.slice(offset, offset + length);
+ return [slice, offset + length];
+}
+
+function readVarSlice(buffer: Uint8Array, offset: number): [Uint8Array, number] {
+ const [length, newOffset] = readVarInt(buffer, offset);
+ return readSlice(buffer, newOffset, length);
+}
+
+function readVector(buffer: Uint8Array, offset: number): [Uint8Array[], number] {
+ const [count, newOffset] = readVarInt(buffer, offset);
+ let updatedOffset = newOffset;
+ const vector: Uint8Array[] = [];
+
+ for (let i = 0; i < count; i++) {
+ const [slice, nextOffset] = readVarSlice(buffer, updatedOffset);
+ vector.push(slice);
+ updatedOffset = nextOffset;
+ }
+
+ return [vector, updatedOffset];
+}
+
// Inversed the opcodes object from https://github.com/mempool/mempool/blob/14e49126c3ca8416a8d7ad134a95c5e090324d69/backend/src/utils/bitcoin-script.ts#L1
const opcodes = {
0: 'OP_0',
From 494be165ad70b3e2f8d3b82afde710e86424c202 Mon Sep 17 00:00:00 2001
From: wiz
Date: Tue, 4 Mar 2025 09:25:39 -1000
Subject: [PATCH 079/534] Update latest tag on dockerhub
---
.github/workflows/on-tag.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml
index 8a846631c..ba9e1eb7b 100644
--- a/.github/workflows/on-tag.yml
+++ b/.github/workflows/on-tag.yml
@@ -105,7 +105,7 @@ jobs:
--cache-to "type=local,dest=/tmp/.buildx-cache,mode=max" \
--platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
- # --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
+ --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--build-context rustgbt=./rust \
--build-context backend=./backend \
--output "type=registry,push=true" \
From c4e22a6225c04547666e3a0d962b3f9d6cab5db9 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 5 Mar 2025 04:18:31 +0000
Subject: [PATCH 080/534] disabled ON UPDATE for blocks_audits time field
---
backend/src/api/database-migration.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts
index 4f43bd9d2..299cd309b 100644
--- a/backend/src/api/database-migration.ts
+++ b/backend/src/api/database-migration.ts
@@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
import { RowDataPacket } from 'mysql2';
class DatabaseMigration {
- private static currentVersion = 95;
+ private static currentVersion = 96;
private queryTimeout = 3600_000;
private statisticsAddedIndexed = false;
private uniqueLogs: string[] = [];
@@ -1130,6 +1130,11 @@ class DatabaseMigration {
await this.$executeQuery('ALTER TABLE blocks ADD INDEX `definition_hash` (`definition_hash`)');
await this.updateToSchemaVersion(95);
}
+
+ if (databaseSchemaVersion < 96) {
+ await this.$executeQuery(`ALTER TABLE blocks_audits MODIFY time timestamp NOT NULL DEFAULT 0`);
+ await this.updateToSchemaVersion(96);
+ }
}
/**
From ad140dc60a32a9ced5d2772d355731f60f2553c6 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Wed, 5 Mar 2025 15:29:54 +0100
Subject: [PATCH 081/534] Tapscript multisig parsing
---
frontend/src/app/shared/script.utils.ts | 61 +++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts
index 731e0051b..62a7a5845 100644
--- a/frontend/src/app/shared/script.utils.ts
+++ b/frontend/src/app/shared/script.utils.ts
@@ -251,6 +251,11 @@ export function detectScriptTemplate(type: ScriptType, script_asm: string, witne
return ScriptTemplates.multisig(multisig.m, multisig.n);
}
+ const tapscriptMultisig = parseTapscriptMultisig(script_asm);
+ if (tapscriptMultisig) {
+ return ScriptTemplates.multisig(tapscriptMultisig.m, tapscriptMultisig.n);
+ }
+
return;
}
@@ -299,6 +304,62 @@ export function parseMultisigScript(script: string): undefined | { m: number, n:
return { m, n };
}
+export function parseTapscriptMultisig(script: string): undefined | { m: number, n: number } {
+ if (!script) {
+ return;
+ }
+
+ const ops = script.split(' ');
+ // At minimum, one pubkey group (3 tokens) + m push + final opcode = 5 tokens
+ if (ops.length < 5) return;
+
+ const finalOp = ops.pop();
+ if (finalOp !== 'OP_NUMEQUAL' && finalOp !== 'OP_GREATERTHANOREQUAL') {
+ return;
+ }
+
+ let m: number;
+ if (['OP_PUSHBYTES_1', 'OP_PUSHBYTES_2'].includes(ops[ops.length - 2])) {
+ const data = ops.pop();
+ ops.pop();
+ m = parseInt(data.match(/../g).reverse().join(''), 16);
+ } else if (ops[ops.length - 1].startsWith('OP_PUSHNUM_') || ops[ops.length - 1] === 'OP_0') {
+ m = parseInt(ops.pop().match(/[0-9]+/)?.[0], 10);
+ } else {
+ return;
+ }
+
+ if (ops.length % 3 !== 0) {
+ return;
+ }
+ const n = ops.length / 3;
+ if (n < 1) {
+ return;
+ }
+
+ for (let i = 0; i < n; i++) {
+ const push = ops.shift();
+ const pubkey = ops.shift();
+ const sigOp = ops.shift();
+
+ if (push !== 'OP_PUSHBYTES_32') {
+ return;
+ }
+ if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) {
+ return;
+ }
+ if (sigOp !== (i === 0 ? 'OP_CHECKSIG' : 'OP_CHECKSIGADD')) {
+ return;
+ }
+ }
+
+ if (ops.length) {
+ return;
+ }
+
+ return { m, n };
+}
+
export function getVarIntLength(n: number): number {
if (n < 0xfd) {
return 1;
From 55c09efb580f87849bc2c8c6d4a81b3345b0b17d Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Thu, 6 Mar 2025 02:42:17 +0000
Subject: [PATCH 082/534] be your own explorer
---
frontend/src/app/components/about/about.component.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html
index 433fe1abb..3bd8960f5 100644
--- a/frontend/src/app/components/about/about.component.html
+++ b/frontend/src/app/components/about/about.component.html
@@ -12,6 +12,7 @@
The Mempool Open Source Project ®
Our mempool and blockchain explorer for the Bitcoin community, focusing on the transaction fee market and multi-layer ecosystem, completely self-hosted without any trusted third-parties.
+
Be your own explorer™
From caef3c49a6fcbe2115bd17adcf8dae4fdf9ca72d Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Thu, 6 Mar 2025 02:44:45 +0000
Subject: [PATCH 083/534] be your own explorer faq
---
.../shared/components/global-footer/global-footer.component.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html
index 24e5c73ae..72fd2ce01 100644
--- a/frontend/src/app/shared/components/global-footer/global-footer.component.html
+++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html
@@ -85,6 +85,7 @@
What is a block explorer?
What is a mempool explorer?
Why isn't my transaction confirming?
+ Be your own explorer™
More FAQs »
Research
From 0b1895664b43af0c2d0b78a8e4e2a868d5bfea8a Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Thu, 6 Mar 2025 03:52:20 +0000
Subject: [PATCH 084/534] change staging proxy from fmt to va1
---
frontend/proxy.conf.staging.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/proxy.conf.staging.js b/frontend/proxy.conf.staging.js
index 260b222c0..0165bed96 100644
--- a/frontend/proxy.conf.staging.js
+++ b/frontend/proxy.conf.staging.js
@@ -3,10 +3,10 @@ const fs = require('fs');
let PROXY_CONFIG = require('./proxy.conf');
PROXY_CONFIG.forEach(entry => {
- const hostname = process.env.CYPRESS_REROUTE_TESTNET === 'true' ? 'mempool-staging.fra.mempool.space' : 'node201.fmt.mempool.space';
+ const hostname = process.env.CYPRESS_REROUTE_TESTNET === 'true' ? 'mempool-staging.fra.mempool.space' : 'node201.va1.mempool.space';
console.log(`e2e tests running against ${hostname}`);
entry.target = entry.target.replace("mempool.space", hostname);
- entry.target = entry.target.replace("liquid.network", "liquid-staging.fmt.mempool.space");
+ entry.target = entry.target.replace("liquid.network", "liquid-staging.va1.mempool.space");
});
module.exports = PROXY_CONFIG;
From 3b9d9864cf9bf4f72f9b89b57aa4c5b28fe531f2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 7 Mar 2025 02:22:43 +0000
Subject: [PATCH 085/534] Bump mysql2 from 3.12.0 to 3.13.0 in /backend
Bumps [mysql2](https://github.com/sidorares/node-mysql2) from 3.12.0 to 3.13.0.
- [Release notes](https://github.com/sidorares/node-mysql2/releases)
- [Changelog](https://github.com/sidorares/node-mysql2/blob/master/Changelog.md)
- [Commits](https://github.com/sidorares/node-mysql2/compare/v3.12.0...v3.13.0)
---
updated-dependencies:
- dependency-name: mysql2
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
backend/package-lock.json | 14 +++++++-------
backend/package.json | 2 +-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 1aaa77f85..a4963d6f0 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -17,7 +17,7 @@
"crypto-js": "~4.2.0",
"express": "~4.21.1",
"maxmind": "~4.3.11",
- "mysql2": "~3.12.0",
+ "mysql2": "~3.13.0",
"redis": "^4.7.0",
"rust-gbt": "file:./rust-gbt",
"socks-proxy-agent": "~7.0.0",
@@ -6173,9 +6173,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mysql2": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
- "integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.13.0.tgz",
+ "integrity": "sha512-M6DIQjTqKeqXH5HLbLMxwcK5XfXHw30u5ap6EZmu7QVmcF/gnh2wS/EOiQ4MTbXz/vQeoXrmycPlVRM00WSslg==",
"license": "MIT",
"dependencies": {
"aws-ssl-profiles": "^1.1.1",
@@ -12337,9 +12337,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mysql2": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
- "integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.13.0.tgz",
+ "integrity": "sha512-M6DIQjTqKeqXH5HLbLMxwcK5XfXHw30u5ap6EZmu7QVmcF/gnh2wS/EOiQ4MTbXz/vQeoXrmycPlVRM00WSslg==",
"requires": {
"aws-ssl-profiles": "^1.1.1",
"denque": "^2.1.0",
diff --git a/backend/package.json b/backend/package.json
index efc5a4501..bcbc0f256 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -46,7 +46,7 @@
"crypto-js": "~4.2.0",
"express": "~4.21.1",
"maxmind": "~4.3.11",
- "mysql2": "~3.12.0",
+ "mysql2": "~3.13.0",
"rust-gbt": "file:./rust-gbt",
"redis": "^4.7.0",
"socks-proxy-agent": "~7.0.0",
From 9d711c336a51a05b55b7a7b56ec78d2ad2af2b6d Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Sun, 9 Mar 2025 11:42:53 +0100
Subject: [PATCH 086/534] retreive -> retrieve
---
backend/src/api/blocks.ts | 8 ++++----
backend/src/repositories/HashratesRepository.ts | 2 +-
.../accelerate-checkout/accelerate-checkout.component.ts | 6 +++---
frontend/src/app/docs/api-docs/api-docs-data.ts | 2 +-
frontend/src/app/services/services-api.service.ts | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts
index 102601594..beefc825b 100644
--- a/backend/src/api/blocks.ts
+++ b/backend/src/api/blocks.ts
@@ -1469,11 +1469,11 @@ class Blocks {
if (rows && Array.isArray(rows)) {
return rows.map(r => r.definition_hash);
} else {
- logger.debug(`Unable to retreive list of blocks.definition_hash from db (no result)`);
+ logger.debug(`Unable to retrieve list of blocks.definition_hash from db (no result)`);
return null;
}
} catch (e) {
- logger.debug(`Unable to retreive list of blocks.definition_hash from db (exception: ${e})`);
+ logger.debug(`Unable to retrieve list of blocks.definition_hash from db (exception: ${e})`);
return null;
}
}
@@ -1484,11 +1484,11 @@ class Blocks {
if (rows && Array.isArray(rows)) {
return rows.map(r => r.hash);
} else {
- logger.debug(`Unable to retreive list of blocks for definition hash ${definitionHash} from db (no result)`);
+ logger.debug(`Unable to retrieve list of blocks for definition hash ${definitionHash} from db (no result)`);
return null;
}
} catch (e) {
- logger.debug(`Unable to retreive list of blocks for definition hash ${definitionHash} from db (exception: ${e})`);
+ logger.debug(`Unable to retrieve list of blocks for definition hash ${definitionHash} from db (exception: ${e})`);
return null;
}
}
diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts
index ec44afebe..93aa2d53f 100644
--- a/backend/src/repositories/HashratesRepository.ts
+++ b/backend/src/repositories/HashratesRepository.ts
@@ -93,7 +93,7 @@ class HashratesRepository {
const [rows]: any[] = await DB.query(query);
return rows.map(row => row.timestamp);
} catch (e) {
- logger.err('Cannot retreive indexed weekly hashrate timestamps. Reason: ' + (e instanceof Error ? e.message : e), logger.tags.mining);
+ logger.err('Cannot retrieve indexed weekly hashrate timestamps. Reason: ' + (e instanceof Error ? e.message : e), logger.tags.mining);
throw e;
}
}
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
index ac6c7f147..bf70aebd3 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
@@ -525,7 +525,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
if (tokenResult?.status === 'OK') {
const card = tokenResult.details?.card;
if (!card || !card.brand || !card.expMonth || !card.expYear || !card.last4) {
- console.error(`Cannot retreive payment card details`);
+ console.error(`Cannot retrieve payment card details`);
this.accelerateError = 'apple_pay_no_card_details';
this.processing = false;
return;
@@ -643,7 +643,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
if (tokenResult?.status === 'OK') {
const card = tokenResult.details?.card;
if (!card || !card.brand || !card.expMonth || !card.expYear || !card.last4) {
- console.error(`Cannot retreive payment card details`);
+ console.error(`Cannot retrieve payment card details`);
this.accelerateError = 'apple_pay_no_card_details';
this.processing = false;
return;
@@ -936,7 +936,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.loadingBtcpayInvoice = true;
this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.tx.txid, this.userBid).pipe(
switchMap(response => {
- return this.servicesApiService.retreiveInvoice$(response.btcpayInvoiceId);
+ return this.servicesApiService.retrieveInvoice$(response.btcpayInvoiceId);
}),
catchError(error => {
console.log(error);
diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts
index c32baa3f7..5e9608bdf 100644
--- a/frontend/src/app/docs/api-docs/api-docs-data.ts
+++ b/frontend/src/app/docs/api-docs/api-docs-data.ts
@@ -11803,7 +11803,7 @@ export const restApiDocsData = [
fragment: "accelerator-cancel",
title: "POST Cancel Acceleration (Pro)",
description: {
- default: "Sends a request to cancel an acceleration in the accelerating status. You can retreive eligible acceleration id using the history endpoint GET /api/v1/services/accelerator/history?status=accelerating."
+ default: "
Sends a request to cancel an acceleration in the accelerating status. You can retrieve eligible acceleration id using the history endpoint GET /api/v1/services/accelerator/history?status=accelerating."
},
urlString: "/v1/services/accelerator/cancel",
showConditions: [""],
diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts
index 59dc92358..a9550e731 100644
--- a/frontend/src/app/services/services-api.service.ts
+++ b/frontend/src/app/services/services-api.service.ts
@@ -213,7 +213,7 @@ export class ServicesApiServices {
return this.httpClient.post(`${this.stateService.env.SERVICES_API}/payments/bitcoin`, params);
}
- retreiveInvoice$(invoiceId: string): Observable {
+ retrieveInvoice$(invoiceId: string): Observable {
return this.httpClient.get(`${this.stateService.env.SERVICES_API}/payments/bitcoin/invoice?id=${invoiceId}`);
}
From 658151e0e8cee063d5f60852aaf0f1d9db1f38cf Mon Sep 17 00:00:00 2001
From: wiz
Date: Tue, 11 Mar 2025 12:46:00 +0900
Subject: [PATCH 087/534] ops: Update electrs patch path for FreeBSD prod build
---
production/install | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/production/install b/production/install
index 05b12c08e..68eac6e5a 100755
--- a/production/install
+++ b/production/install
@@ -1314,7 +1314,7 @@ if [ "${BITCOIN_ELECTRS_INSTALL}" = ON ];then
case $OS in
FreeBSD)
echo "[*] Patching Bitcoin Electrs code for FreeBSD"
- osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_HOME}/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\""
+ osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_HOME}/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\""
#osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/new_index/\" && sed -i.bak -e s/Snappy/None/ db.rs && rm db.rs.bak"
#osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/bin/\" && sed -i.bak -e 's/from_secs(5)/from_secs(1)/' electrs.rs && rm electrs.rs.bak"
;;
@@ -1364,7 +1364,7 @@ if [ "${ELEMENTS_ELECTRS_INSTALL}" = ON ];then
case $OS in
FreeBSD)
echo "[*] Patching Liquid Electrs code for FreeBSD"
- osSudo "${ELEMENTS_USER}" sh -c "cd \"${ELEMENTS_HOME}/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\""
+ osSudo "${ELEMENTS_USER}" sh -c "cd \"${ELEMENTS_HOME}/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\""
;;
Debian)
;;
From 7adc0083af973c65c321e9bcb96bfa371c87e401 Mon Sep 17 00:00:00 2001
From: wiz
Date: Tue, 11 Mar 2025 14:08:51 +0900
Subject: [PATCH 088/534] ops: Modify prod install to run even if mysql exists
---
production/install | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/production/install b/production/install
index 68eac6e5a..487850b37 100755
--- a/production/install
+++ b/production/install
@@ -562,7 +562,7 @@ zfsCreateFilesystems()
zfs create -o "mountpoint=${MINFEE_HOME}" "${ZPOOL}/minfee"
zfs create -o "mountpoint=${ELECTRS_HOME}" "${ZPOOL}/electrs"
zfs create -o "mountpoint=${MEMPOOL_HOME}" "${ZPOOL}/mempool"
- zfs create -o "mountpoint=${MYSQL_HOME}" "${ZPOOL}/mysql"
+ zfs create -o "mountpoint=${MYSQL_HOME}" "${ZPOOL}/mysql" || true
zfs create -o "mountpoint=${BITCOIN_ELECTRS_HOME}" "${ZPOOL}/bitcoin/electrs"
@@ -1907,34 +1907,34 @@ esac
sleep 10
mysql << _EOF_
-create database mempool;
+create database if not exists mempool;
grant all on mempool.* to '${MEMPOOL_MAINNET_USER}'@'localhost' identified by '${MEMPOOL_MAINNET_PASS}';
-create database mempool_testnet;
+create database if not exists mempool_testnet;
grant all on mempool_testnet.* to '${MEMPOOL_TESTNET_USER}'@'localhost' identified by '${MEMPOOL_TESTNET_PASS}';
-create database mempool_testnet4;
+create database if not exists mempool_testnet4;
grant all on mempool_testnet4.* to '${MEMPOOL_TESTNET4_USER}'@'localhost' identified by '${MEMPOOL_TESTNET4_PASS}';
-create database mempool_signet;
+create database if not exists mempool_signet;
grant all on mempool_signet.* to '${MEMPOOL_SIGNET_USER}'@'localhost' identified by '${MEMPOOL_SIGNET_PASS}';
-create database mempool_mainnet_lightning;
+create database if not exists mempool_mainnet_lightning;
grant all on mempool_mainnet_lightning.* to '${MEMPOOL_MAINNET_LIGHTNING_USER}'@'localhost' identified by '${MEMPOOL_MAINNET_LIGHTNING_PASS}';
-create database mempool_testnet_lightning;
+create database if not exists mempool_testnet_lightning;
grant all on mempool_testnet_lightning.* to '${MEMPOOL_TESTNET_LIGHTNING_USER}'@'localhost' identified by '${MEMPOOL_TESTNET_LIGHTNING_PASS}';
-create database mempool_signet_lightning;
+create database if not exists mempool_signet_lightning;
grant all on mempool_signet_lightning.* to '${MEMPOOL_SIGNET_LIGHTNING_USER}'@'localhost' identified by '${MEMPOOL_SIGNET_LIGHTNING_PASS}';
-create database mempool_liquid;
+create database if not exists mempool_liquid;
grant all on mempool_liquid.* to '${MEMPOOL_LIQUID_USER}'@'localhost' identified by '${MEMPOOL_LIQUID_PASS}';
-create database mempool_liquidtestnet;
+create database if not exists mempool_liquidtestnet;
grant all on mempool_liquidtestnet.* to '${MEMPOOL_LIQUIDTESTNET_USER}'@'localhost' identified by '${MEMPOOL_LIQUIDTESTNET_PASS}';
-create database mempool_bisq;
+create database if not exists mempool_bisq;
grant all on mempool_bisq.* to '${MEMPOOL_BISQ_USER}'@'localhost' identified by '${MEMPOOL_BISQ_PASS}';
_EOF_
From 636b4c0da72d31ddde3e27bdfa7d1e078d62327c Mon Sep 17 00:00:00 2001
From: wiz
Date: Tue, 11 Mar 2025 15:46:03 +0900
Subject: [PATCH 089/534] ops: Add missing if check for CLN in prod install
---
production/install | 3 +++
1 file changed, 3 insertions(+)
diff --git a/production/install b/production/install
index 487850b37..7c84e5956 100755
--- a/production/install
+++ b/production/install
@@ -1378,6 +1378,8 @@ fi
# Core Lightning for Bitcoin #
##############################
+if [ "${CLN_INSTALL}" = ON ];then
+
echo "[*] Installing Core Lightning"
case $OS in
FreeBSD)
@@ -1418,6 +1420,7 @@ case $OS in
;;
esac
+fi
#####################
# Bisq installation #
From 9bef19449fd24dc0e6a47a24a58f7a7dcc7bc051 Mon Sep 17 00:00:00 2001
From: wiz
Date: Tue, 11 Mar 2025 16:37:14 +0900
Subject: [PATCH 090/534] ops: Comment out old keybase commands in build script
---
production/mempool-build-all | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/production/mempool-build-all b/production/mempool-build-all
index 377deb316..10ad179ea 100755
--- a/production/mempool-build-all
+++ b/production/mempool-build-all
@@ -173,7 +173,7 @@ for repo in $frontend_repos;do
done
# notify everyone
-echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.dev
-echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general "mempool.ops.${LOCATION}"
+#echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.dev
+#echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general "mempool.ops.${LOCATION}"
exit 0
From c79ef93413a46115522e801cb40590df8a2afe37 Mon Sep 17 00:00:00 2001
From: wiz
Date: Tue, 11 Mar 2025 16:38:26 +0900
Subject: [PATCH 091/534] ops: Bump prod to bitcoin v28.1 + elements 23.2.6
---
production/install | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/production/install b/production/install
index 7c84e5956..f3bebb4e7 100755
--- a/production/install
+++ b/production/install
@@ -357,7 +357,7 @@ BITCOIN_REPO_URL=https://github.com/bitcoin/bitcoin
BITCOIN_REPO_NAME=bitcoin
BITCOIN_REPO_BRANCH=master
#BITCOIN_LATEST_RELEASE=$(curl -s https://api.github.com/repos/bitcoin/bitcoin/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
-BITCOIN_LATEST_RELEASE=v28.0
+BITCOIN_LATEST_RELEASE=v28.1
echo -n '.'
BISQ_REPO_URL=https://github.com/bisq-network/bisq
@@ -378,7 +378,7 @@ ELEMENTS_REPO_URL=https://github.com/ElementsProject/elements
ELEMENTS_REPO_NAME=elements
ELEMENTS_REPO_BRANCH=master
#ELEMENTS_LATEST_RELEASE=$(curl -s https://api.github.com/repos/ElementsProject/elements/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
-ELEMENTS_LATEST_RELEASE=elements-22.1.1
+ELEMENTS_LATEST_RELEASE=elements-23.2.6
echo -n '.'
BITCOIN_ELECTRS_REPO_URL=https://github.com/mempool/electrs
From 65b276678fb8c64ff0b2ac28d0384962a021b3dc Mon Sep 17 00:00:00 2001
From: wiz
Date: Wed, 12 Mar 2025 09:35:55 +0900
Subject: [PATCH 092/534] ops: Add negative balance check to check script
---
production/check | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/production/check b/production/check
index 3f31e67ee..3da5e920d 100755
--- a/production/check
+++ b/production/check
@@ -18,28 +18,37 @@ check_mempoolfoss_frontend_md5_hash() {
check_mempool_electrs_git_hash() {
echo -n $(curl -s -i https://node${1}.${2}.mempool.space/api/mempool|grep -i x-powered-by|cut -d ' ' -f3|cut -d '-' -f3|tr -d '\r'|tr -d '\n')
}
+check_mempool_electrs_negative_balance() {
+ echo -n $(curl -s https://node${1}.${2}.mempool.space/api/address/35Ty15fzBPGQvKnXZMLYvr41Fq2FTdU54a|jq .chain_stats.spent_txo_sum|tr -d '\r'|tr -d '\n')
+}
check_liquid_electrs_git_hash() {
echo -n $(curl -s -i --connect-to "::node${1}.${2}.mempool.space:443" https://liquid.network/api/mempool|grep -i x-powered-by|cut -d ' ' -f3|cut -d '-' -f3|tr -d '\r'|tr -d '\n')
}
-for site in fmt va1 fra tk7;do
+check_contributors_md5_hash() {
+ echo -n $(curl -s --connect-to "::node${1}.${2}.mempool.space:443" https://mempool.space/api/v1/contributors|md5|cut -c1-8)
+}
+for site in va1 fra tk7 fmt;do
echo "${site}"
for node in 201 202 203 204 205 206 207 208 209 210 211 212 213 214;do
[ "${site}" = "fmt" ] && [ "${node}" -gt 206 ] && continue
[ "${site}" = "tk7" ] && [ "${node}" -gt 206 ] && continue
echo -n "node${node}.${site}: "
- check_mempoolspace_frontend_git_hash $node $site
- echo -n " "
- check_mempoolfoss_frontend_git_hash $node $site
- echo -n " "
- check_mempoolfoss_backend_git_hash $node $site
- echo -n " "
- check_mempoolspace_frontend_md5_hash $node $site
- echo -n " "
- check_mempoolfoss_frontend_md5_hash $node $site
- echo -n " "
+# check_mempoolspace_frontend_git_hash $node $site
+# echo -n " "
+# check_mempoolfoss_frontend_git_hash $node $site
+# echo -n " "
+# check_mempoolfoss_backend_git_hash $node $site
+# echo -n " "
+# check_mempoolspace_frontend_md5_hash $node $site
+# echo -n " "
+# check_mempoolfoss_frontend_md5_hash $node $site
+# echo -n " "
check_mempool_electrs_git_hash $node $site
echo -n " "
- check_liquid_electrs_git_hash $node $site
+# check_liquid_electrs_git_hash $node $site
+# echo -n " "
+# check_contributors_md5_hash $node $site
+ check_mempool_electrs_negative_balance $node $site
echo
done
done
From 305d931d5c3fc5b87b1a2c08efbb4f634a3dee7f Mon Sep 17 00:00:00 2001
From: wiz
Date: Wed, 12 Mar 2025 09:43:00 +0900
Subject: [PATCH 093/534] ops: Add more sites to check script
---
production/check | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/production/check b/production/check
index 3da5e920d..d1bba7826 100755
--- a/production/check
+++ b/production/check
@@ -27,9 +27,12 @@ check_liquid_electrs_git_hash() {
check_contributors_md5_hash() {
echo -n $(curl -s --connect-to "::node${1}.${2}.mempool.space:443" https://mempool.space/api/v1/contributors|md5|cut -c1-8)
}
-for site in va1 fra tk7 fmt;do
+for site in va1 fra tk7 sg1 hnl;do
echo "${site}"
for node in 201 202 203 204 205 206 207 208 209 210 211 212 213 214;do
+ [ "${site}" = "fmt" ] && [ "${node}" = 203 ] && continue
+ [ "${site}" = "sg1" ] && [ "${node}" -gt 204 ] && continue
+ [ "${site}" = "hnl" ] && [ "${node}" -gt 204 ] && continue
[ "${site}" = "fmt" ] && [ "${node}" -gt 206 ] && continue
[ "${site}" = "tk7" ] && [ "${node}" -gt 206 ] && continue
echo -n "node${node}.${site}: "
From a152afb4af6aedc111b08c1e25ed9813e22656f9 Mon Sep 17 00:00:00 2001
From: wiz
Date: Wed, 12 Mar 2025 11:51:03 +0900
Subject: [PATCH 094/534] ops: Fix nginx conf for elements unix socket paths
---
production/nginx/nginx.conf | 4 ++--
production/nginx/server-esplora.conf | 4 ++--
production/nginx/upstream-esplora.conf | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/production/nginx/nginx.conf b/production/nginx/nginx.conf
index 81c0c01d5..790bd6d73 100644
--- a/production/nginx/nginx.conf
+++ b/production/nginx/nginx.conf
@@ -99,8 +99,8 @@ http {
set $mempoolTestnet "http://mempool-liquid-testnet";
# for blockstream/esplora daemon, see upstream-esplora.conf
- set $esploraMainnet "http://esplora-liquid-mainnet";
- set $esploraTestnet "http://esplora-liquid-testnet";
+ set $esploraMainnet "http://esplora-elements-liquid";
+ set $esploraTestnet "http://esplora-elements-liquidtestnet";
# filesystem paths
root /mempool/public_html/liquid/;
diff --git a/production/nginx/server-esplora.conf b/production/nginx/server-esplora.conf
index 38cdb0bc2..fdbea6bd5 100644
--- a/production/nginx/server-esplora.conf
+++ b/production/nginx/server-esplora.conf
@@ -9,7 +9,7 @@ server {
listen 127.0.0.1:4001;
access_log /dev/null;
location / {
- proxy_pass http://esplora-liquid-mainnet;
+ proxy_pass http://esplora-elements-liquid;
}
}
server {
@@ -30,6 +30,6 @@ server {
listen 127.0.0.1:4004;
access_log /dev/null;
location / {
- proxy_pass http://esplora-liquid-testnet;
+ proxy_pass http://esplora-elements-liquidtestnet;
}
}
diff --git a/production/nginx/upstream-esplora.conf b/production/nginx/upstream-esplora.conf
index 88ffa11bd..80b76df2d 100644
--- a/production/nginx/upstream-esplora.conf
+++ b/production/nginx/upstream-esplora.conf
@@ -1,7 +1,7 @@
upstream esplora-bitcoin-mainnet {
server unix:/bitcoin/socket/esplora-bitcoin-mainnet fail_timeout=10s max_fails=10 weight=99999;
}
-upstream esplora-liquid-mainnet {
+upstream esplora-elements-liquid {
server unix:/elements/socket/esplora-elements-liquid fail_timeout=10s max_fails=10 weight=99999;
}
upstream esplora-bitcoin-testnet {
@@ -13,6 +13,6 @@ upstream esplora-bitcoin-testnet4 {
upstream esplora-bitcoin-signet {
server unix:/bitcoin/socket/esplora-bitcoin-signet fail_timeout=10s max_fails=10 weight=99999;
}
-upstream esplora-liquid-testnet {
+upstream esplora-elements-liquidtestnet {
server unix:/elements/socket/esplora-elements-liquidtestnet fail_timeout=10s max_fails=10 weight=99999;
}
From 7d9e275803e90b621b4419561962086e5f41a3f2 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 12 Mar 2025 14:56:15 +0100
Subject: [PATCH 095/534] [accelerator] show square receipt if available
---
.../accelerate-checkout.component.html | 19 +++++++++++++++++--
.../accelerate-checkout.component.ts | 16 ++++++++--------
2 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
index 4594fd9fc..2038d4b6c 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
@@ -567,14 +567,29 @@
} @else if (step === 'success') {
-
Your transaction is being accelerated!
+
+ @if (accelerationResponse) {
+ Your transaction is being accelerated!
+ } @else {
+ Transaction is already being accelerated!
+ }
+
- Your transaction has been accepted for acceleration by our mining pool partners.
+ @if (accelerationResponse) {
+ Your transaction has been accepted for acceleration by our mining pool partners.
+ } @else {
+ Transaction has already been accepted for acceleration by our mining pool partners.
+ }
+ @if (accelerationResponse?.receiptUrl) {
+
+ }
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
index ac6c7f147..06d2fa4cd 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
@@ -87,6 +87,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
math = Math;
isMobile: boolean = window.innerWidth <= 767.98;
isProdDomain = false;
+ accelerationResponse: { receiptUrl: string | null } | undefined;
private _step: CheckoutStep = 'summary';
simpleMode: boolean = true;
@@ -194,11 +195,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.scrollToElement('acceleratePreviewAnchor', 'start');
}
if (changes.accelerating && this.accelerating) {
- if (this.step === 'processing' || this.step === 'paid') {
- this.moveToStep('success', true);
- } else { // Edge case where the transaction gets accelerated by someone else or on another session
- this.closeModal();
- }
+ this.moveToStep('success', true);
}
}
@@ -541,7 +538,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
`accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`,
costUSD
).subscribe({
- next: () => {
+ next: (response) => {
+ this.accelerationResponse = response;
this.processing = false;
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
@@ -668,7 +666,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
costUSD,
verificationToken.userChallenged
).subscribe({
- next: () => {
+ next: (response) => {
+ this.accelerationResponse = response;
this.processing = false;
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
@@ -777,7 +776,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
costUSD,
verificationToken.userChallenged
).subscribe({
- next: () => {
+ next: (response) => {
+ this.accelerationResponse = response;
this.processing = false;
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
From cac404ae9bbce0c6192fddf3a99bb8c79483f134 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 12 Mar 2025 15:26:44 +0100
Subject: [PATCH 096/534] [accelerator] make sure we cannot go back from
'success' step
---
.../accelerate-checkout/accelerate-checkout.component.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
index 06d2fa4cd..0db40af82 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
@@ -200,7 +200,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
moveToStep(step: CheckoutStep, force: boolean = false): void {
- if (this.isCheckoutLocked > 0 && !force) {
+ if (this.isCheckoutLocked > 0 && !force || this.step === 'success') {
return;
}
this.processing = false;
From 1121377c7a888d8a1d12859cf9488d3db4b56249 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 12 Mar 2025 15:27:00 +0100
Subject: [PATCH 097/534] [accelerator] add missing response from cashapp
payment
---
.../accelerate-checkout/accelerate-checkout.component.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
index 0db40af82..7fc1e88ef 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts
@@ -870,7 +870,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
tokenResult.details.cashAppPay.referenceId,
costUSD
).subscribe({
- next: () => {
+ next: (response) => {
+ this.accelerationResponse = response;
this.processing = false;
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
From 062c5ca03a0d1bf4e8678dc717b9df5b228a4955 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Wed, 12 Mar 2025 16:47:31 +0100
Subject: [PATCH 098/534] Trim input data in tx preview
---
.../src/app/components/transaction/transaction-raw.component.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.ts b/frontend/src/app/components/transaction/transaction-raw.component.ts
index 5ce170e12..f7ae4a751 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.ts
+++ b/frontend/src/app/components/transaction/transaction-raw.component.ts
@@ -82,7 +82,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.resetState();
this.isLoading = true;
try {
- const { tx, hex } = decodeRawTransaction(this.pushTxForm.get('txRaw').value, this.stateService.network);
+ const { tx, hex } = decodeRawTransaction(this.pushTxForm.get('txRaw').value.trim(), this.stateService.network);
await this.fetchPrevouts(tx);
await this.fetchCpfpInfo(tx);
this.processTransaction(tx, hex);
From 4dff3adf1169d056f35acd5b3c28bb5fadbd850d Mon Sep 17 00:00:00 2001
From: natsoni
Date: Wed, 12 Mar 2025 16:49:38 +0100
Subject: [PATCH 099/534] Redirect to tx page 2 seconds after broadcast
---
.../transaction-raw.component.html | 11 ++++---
.../transaction-raw.component.scss | 8 +++++
.../transaction/transaction-raw.component.ts | 31 ++++++++++++-------
3 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html
index 3bd8ee6d2..35701889b 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.html
+++ b/frontend/src/app/components/transaction/transaction-raw.component.html
@@ -30,7 +30,6 @@
- Broadcasted
✕
@@ -40,14 +39,18 @@
-
+
-
+
This transaction is stored locally in your browser. Broadcast it to add it to the mempool.
+
+ Redirecting to transaction page...
+
- Broadcast
+ Broadcast
+ Broadcasted
@if (!hasPrevouts) {
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.scss b/frontend/src/app/components/transaction/transaction-raw.component.scss
index 5bbe5601e..a4b386cee 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.scss
+++ b/frontend/src/app/components/transaction/transaction-raw.component.scss
@@ -191,4 +191,12 @@
.no-cursor {
cursor: default !important;
pointer-events: none;
+}
+
+.btn-broadcast {
+ margin-left: 5px;
+ @media (max-width: 567px) {
+ margin-left: 0;
+ margin-top: 5px;
+ }
}
\ No newline at end of file
diff --git a/frontend/src/app/components/transaction/transaction-raw.component.ts b/frontend/src/app/components/transaction/transaction-raw.component.ts
index f7ae4a751..2e4dd4868 100644
--- a/frontend/src/app/components/transaction/transaction-raw.component.ts
+++ b/frontend/src/app/components/transaction/transaction-raw.component.ts
@@ -3,7 +3,7 @@ import { Transaction, Vout } from '@interfaces/electrs.interface';
import { StateService } from '../../services/state.service';
import { Filter, toFilters } from '../../shared/filters.utils';
import { decodeRawTransaction, getTransactionFlags, addInnerScriptsToVin, countSigops } from '../../shared/transaction.utils';
-import { firstValueFrom, Subscription } from 'rxjs';
+import { catchError, firstValueFrom, Subscription, switchMap, tap, throwError, timer } from 'rxjs';
import { WebsocketService } from '../../services/websocket.service';
import { ActivatedRoute, Router } from '@angular/router';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
@@ -36,6 +36,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
isLoadingBroadcast: boolean;
errorBroadcast: string;
successBroadcast: boolean;
+ broadcastSubscription: Subscription;
isMobile: boolean;
@ViewChild('graphContainer')
@@ -207,18 +208,22 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
});
}
- async postTx(): Promise
{
+ postTx(): void {
this.isLoadingBroadcast = true;
this.errorBroadcast = null;
- return new Promise((resolve, reject) => {
- this.apiService.postTransaction$(this.rawHexTransaction)
- .subscribe((result) => {
+
+ this.broadcastSubscription = this.apiService.postTransaction$(this.rawHexTransaction).pipe(
+ tap((txid: string) => {
this.isLoadingBroadcast = false;
this.successBroadcast = true;
- this.transaction.txid = result;
- resolve(result);
- },
- (error) => {
+ this.transaction.txid = txid;
+ }),
+ switchMap((txid: string) =>
+ timer(2000).pipe(
+ tap(() => this.router.navigate([this.relativeUrlPipe.transform('/tx/' + txid)])),
+ )
+ ),
+ catchError((error) => {
if (typeof error.error === 'string') {
const matchText = error.error.replace(/\\/g, '').match('"message":"(.*?)"');
this.errorBroadcast = 'Failed to broadcast transaction, reason: ' + (matchText && matchText[1] || error.error);
@@ -226,9 +231,9 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.errorBroadcast = 'Failed to broadcast transaction, reason: ' + error.message;
}
this.isLoadingBroadcast = false;
- reject(this.error);
- });
- });
+ return throwError(() => error);
+ })
+ ).subscribe();
}
resetState() {
@@ -253,6 +258,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.missingPrevouts = [];
this.stateService.markBlock$.next({});
this.mempoolBlocksSubscription?.unsubscribe();
+ this.broadcastSubscription?.unsubscribe();
}
resetForm() {
@@ -308,6 +314,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.mempoolBlocksSubscription?.unsubscribe();
this.flowPrefSubscription?.unsubscribe();
this.stateService.markBlock$.next({});
+ this.broadcastSubscription?.unsubscribe();
}
}
From 00e7e726bc7084933d8e757a69e46049b0eef7bb Mon Sep 17 00:00:00 2001
From: hunicus <93150691+hunicus@users.noreply.github.com>
Date: Thu, 6 Mar 2025 10:31:10 -0500
Subject: [PATCH 100/534] Mark mempool accelerator as registered trademark
---
LICENSE | 2 +-
.../src/app/components/about/about.component.html | 2 +-
.../accelerate-checkout.component.html | 2 +-
.../privacy-policy/privacy-policy.component.html | 12 ++++++------
.../components/svg-images/svg-images.component.html | 4 ++--
.../terms-of-service/terms-of-service.component.html | 8 ++++----
.../trademark-policy/trademark-policy.component.html | 2 +-
.../transaction-details.component.html | 4 ++--
.../src/app/docs/api-docs/api-docs.component.html | 2 +-
9 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/LICENSE b/LICENSE
index 1c368c00a..b6e67e523 100644
--- a/LICENSE
+++ b/LICENSE
@@ -10,7 +10,7 @@ However, this copyright license does not include an implied right or license
to use any trademarks, service marks, logos, or trade names of Mempool Space K.K.
or any other contributor to The Mempool Open Source Project.
-The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®,
+The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®,
Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full
Bitcoin ecosystem™, Mempool Goggles™, the mempool Logo, the mempool Square Logo,
the mempool block visualization Logo, the mempool Blocks Logo, the mempool
diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html
index 3bd8960f5..5b53e94ab 100644
--- a/frontend/src/app/components/about/about.component.html
+++ b/frontend/src/app/components/about/about.component.html
@@ -451,7 +451,7 @@
Trademark Notice
- The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.
+ The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.
While our software is available under an open source software license, the copyright license does not include an implied right or license to use our trademarks. See our Trademark Policy and Guidelines for more details, published on <https://mempool.space/trademark-policy>.
diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
index 4594fd9fc..4ac5aa24e 100644
--- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
+++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html
@@ -158,7 +158,7 @@
- Mempool Accelerator™ fees
+ Mempool Accelerator® fees
diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.html b/frontend/src/app/components/privacy-policy/privacy-policy.component.html
index 06b09ad30..bfd6159c4 100644
--- a/frontend/src/app/components/privacy-policy/privacy-policy.component.html
+++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.html
@@ -45,14 +45,14 @@
- USING MEMPOOL ACCELERATOR™
+ USING MEMPOOL ACCELERATOR®
- If you use Mempool Accelerator™ your acceleration request will be sent to us and relayed to Mempool's mining pool partners. We will store the TXID of the transactions you accelerate with us. We share this information with our mining pool partners, and publicly display accelerated transaction details on our website and APIs. No personal information or account identifiers will be shared with any third party including mining pool partners.
+ If you use Mempool Accelerator® your acceleration request will be sent to us and relayed to Mempool's mining pool partners. We will store the TXID of the transactions you accelerate with us. We share this information with our mining pool partners, and publicly display accelerated transaction details on our website and APIs. No personal information or account identifiers will be shared with any third party including mining pool partners.
- When using Mempool Accelerator™ the mempool.space privacy policy will apply: https://mempool.space/privacy-policy .
+ When using Mempool Accelerator® the mempool.space privacy policy will apply: https://mempool.space/privacy-policy .
-
+
SIGNING UP FOR AN ACCOUNT ON MEMPOOL.SPACE
@@ -67,7 +67,7 @@
If you sign up for a subscription to Mempool Enterprise™ we also collect your company name which is not shared with any third-party.
- If you sign up for an account on mempool.space and use Mempool Accelerator™ Pro your accelerated transactions will be associated with your account for the purposes of accounting.
+ If you sign up for an account on mempool.space and use Mempool Accelerator® Pro your accelerated transactions will be associated with your account for the purposes of accounting.
@@ -101,7 +101,7 @@
We aim to retain your data only as long as necessary:
- An account is considered inactive if all of the following conditions are met: a) No login activity within the past 6 months, b) No active subscriptions associated with the account, c) No Mempool Accelerator™ Pro account credit
+ An account is considered inactive if all of the following conditions are met: a) No login activity within the past 6 months, b) No active subscriptions associated with the account, c) No Mempool Accelerator® Pro account credit
If an account meets the criteria for inactivity as defined above, we will automatically delete the associated account data after a period of 6 months of continuous inactivity, except in the case of payment disputes or account irregularities.
diff --git a/frontend/src/app/components/svg-images/svg-images.component.html b/frontend/src/app/components/svg-images/svg-images.component.html
index 76aa3de85..04b99dea6 100644
--- a/frontend/src/app/components/svg-images/svg-images.component.html
+++ b/frontend/src/app/components/svg-images/svg-images.component.html
@@ -137,7 +137,7 @@
- Mempool Accelerator™
+ Mempool Accelerator®
@@ -695,4 +695,4 @@
-
\ No newline at end of file
+
diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.html b/frontend/src/app/components/terms-of-service/terms-of-service.component.html
index 709605a9f..51f035436 100644
--- a/frontend/src/app/components/terms-of-service/terms-of-service.component.html
+++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.html
@@ -67,9 +67,9 @@
- MEMPOOL ACCELERATOR™
+ MEMPOOL ACCELERATOR®
- Mempool Accelerator™ enables members of the Bitcoin community to submit requests for transaction prioritization.
+ Mempool Accelerator® enables members of the Bitcoin community to submit requests for transaction prioritization.
Mempool will use reasonable commercial efforts to relay user acceleration requests to Mempool's mining pool partners, but it is at the discretion of Mempool and Mempool's mining pool partners to accept acceleration requests.
@@ -84,11 +84,11 @@
- All acceleration payments and Mempool Accelerator™ account credit top-ups are non-refundable.
+ All acceleration payments and Mempool Accelerator® account credit top-ups are non-refundable.
- Mempool Accelerator™ account credit top-ups are prepayment for future accelerations and cannot be withdrawn or transferred.
+ Mempool Accelerator® account credit top-ups are prepayment for future accelerations and cannot be withdrawn or transferred.
diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.html b/frontend/src/app/components/trademark-policy/trademark-policy.component.html
index e12cbb8b2..f7da0a7a4 100644
--- a/frontend/src/app/components/trademark-policy/trademark-policy.component.html
+++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.html
@@ -340,7 +340,7 @@
Also, if you are using our Marks in a way described in the sections "Uses for Which We Are Granting a License," you must include the following trademark attribution at the foot of the webpage where you have used the Mark (or, if in a book, on the credits page), on any packaging or labeling, and on advertising or marketing materials:
- "The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."
+ "The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."
What to Do When You See Abuse
diff --git a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html
index 78bba955c..819e27d89 100644
--- a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html
+++ b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html
@@ -170,7 +170,7 @@
@if (!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) {
@@ -318,4 +318,4 @@
-
\ No newline at end of file
+
diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html
index 75e37a3bd..d359500a0 100644
--- a/frontend/src/app/docs/api-docs/api-docs.component.html
+++ b/frontend/src/app/docs/api-docs/api-docs.component.html
@@ -219,7 +219,7 @@
- To get your transaction confirmed quicker, you will need to increase its effective feerate.
If your transaction was created with RBF enabled, your stuck transaction can simply be replaced with a new one that has a higher fee. Otherwise, if you control any of the stuck transaction's outputs, you can use CPFP to increase your stuck transaction's effective feerate.
If you are not sure how to do RBF or CPFP, work with the tool you used to make the transaction (wallet software, exchange company, etc).
Another option to get your transaction confirmed more quickly is Mempool Accelerator™ .
+ To get your transaction confirmed quicker, you will need to increase its effective feerate.
If your transaction was created with RBF enabled, your stuck transaction can simply be replaced with a new one that has a higher fee. Otherwise, if you control any of the stuck transaction's outputs, you can use CPFP to increase your stuck transaction's effective feerate.
If you are not sure how to do RBF or CPFP, work with the tool you used to make the transaction (wallet software, exchange company, etc).
Another option to get your transaction confirmed more quickly is Mempool Accelerator® .
From 8efea6160135a6284142f911aaf03aab4045aaae Mon Sep 17 00:00:00 2001
From: wiz
Date: Thu, 13 Mar 2025 09:49:40 +0900
Subject: [PATCH 101/534] ops: Add electrs popular-scripts cron jobs
---
production/bitcoin.crontab | 9 +++++++++
production/elements.crontab | 4 ++++
2 files changed, 13 insertions(+)
diff --git a/production/bitcoin.crontab b/production/bitcoin.crontab
index a5bc64241..a17147f7b 100644
--- a/production/bitcoin.crontab
+++ b/production/bitcoin.crontab
@@ -1,7 +1,16 @@
+# start test network daemons on boot
@reboot sleep 5 ; /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
@reboot sleep 5 ; /usr/local/bin/bitcoind -testnet4 >/dev/null 2>&1
@reboot sleep 5 ; /usr/local/bin/bitcoind -signet >/dev/null 2>&1
+
+# start electrs on boot
@reboot sleep 10 ; screen -dmS mainnet /bitcoin/electrs/start mainnet
@reboot sleep 10 ; screen -dmS testnet /bitcoin/electrs/start testnet
@reboot sleep 10 ; screen -dmS testnet4 /bitcoin/electrs/start testnet4
@reboot sleep 10 ; screen -dmS signet /bitcoin/electrs/start signet
+
+# daily update of popular-scripts
+30 03 * * * $HOME/electrs/start testnet4 popular-scripts >/dev/null 2>&1
+31 03 * * * $HOME/electrs/start testnet popular-scripts >/dev/null 2>&1
+32 03 * * * $HOME/electrs/start signet popular-scripts >/dev/null 2>&1
+33 03 * * * $HOME/electrs/start mainnet popular-scripts >/dev/null 2>&1
diff --git a/production/elements.crontab b/production/elements.crontab
index 4f837706e..6590dfbd7 100644
--- a/production/elements.crontab
+++ b/production/elements.crontab
@@ -8,3 +8,7 @@
# hourly asset update and electrs restart
6 * * * * cd $HOME/asset_registry_db && git pull --quiet origin master && cd $HOME/asset_registry_testnet_db && git pull --quiet origin master && killall electrs
+
+# daily update of popular-scripts
+32 03 * * * $HOME/electrs/start liquid popular-scripts >/dev/null 2>&1
+33 03 * * * $HOME/electrs/start liquidtestnet popular-scripts >/dev/null 2>&1
From e94dc67b3187ebfbaa630e43805e2f4be306803a Mon Sep 17 00:00:00 2001
From: natsoni
Date: Thu, 13 Mar 2025 15:46:11 +0100
Subject: [PATCH 102/534] Update tapscript multisig minimum size
---
frontend/src/app/shared/script.utils.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts
index 62a7a5845..df50a4070 100644
--- a/frontend/src/app/shared/script.utils.ts
+++ b/frontend/src/app/shared/script.utils.ts
@@ -310,8 +310,10 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
}
const ops = script.split(' ');
- // At minimum, one pubkey group (3 tokens) + m push + final opcode = 5 tokens
- if (ops.length < 5) return;
+ // At minimum, 2 pubkey group (3 tokens) + m push + final opcode = 8 tokens
+ if (ops.length < 8) {
+ return;
+ }
const finalOp = ops.pop();
if (finalOp !== 'OP_NUMEQUAL' && finalOp !== 'OP_GREATERTHANOREQUAL') {
From 188096e651f745ef8a09d4091d1eb727282a50c4 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Thu, 13 Mar 2025 16:38:53 +0100
Subject: [PATCH 103/534] Add opcodes that can be used for tapscript multisig
---
frontend/src/app/shared/script.utils.ts | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts
index df50a4070..f0c4701db 100644
--- a/frontend/src/app/shared/script.utils.ts
+++ b/frontend/src/app/shared/script.utils.ts
@@ -316,7 +316,7 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
}
const finalOp = ops.pop();
- if (finalOp !== 'OP_NUMEQUAL' && finalOp !== 'OP_GREATERTHANOREQUAL') {
+ if (!['OP_NUMEQUAL', 'OP_NUMEQUALVERIFY', 'OP_GREATERTHANOREQUAL', 'OP_GREATERTHAN', 'OP_EQUAL', 'OP_EQUALVERIFY'].includes(finalOp)) {
return;
}
@@ -331,6 +331,10 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
return;
}
+ if (finalOp === 'OP_GREATERTHAN') {
+ m += 1;
+ }
+
if (ops.length % 3 !== 0) {
return;
}
From 76f31623feffd946c7072a208c990f1f68f6c9cc Mon Sep 17 00:00:00 2001
From: Felipe Knorr Kuhn
Date: Thu, 13 Mar 2025 15:49:45 -0700
Subject: [PATCH 104/534] Don't tag as latest by default
---
.github/workflows/on-tag.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml
index ba9e1eb7b..1447ec4ab 100644
--- a/.github/workflows/on-tag.yml
+++ b/.github/workflows/on-tag.yml
@@ -105,7 +105,6 @@ jobs:
--cache-to "type=local,dest=/tmp/.buildx-cache,mode=max" \
--platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
- --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--build-context rustgbt=./rust \
--build-context backend=./backend \
--output "type=registry,push=true" \
From 4d89cb01bd06a3176f9a19a793dd85fef8eaf93a Mon Sep 17 00:00:00 2001
From: wiz
Date: Fri, 14 Mar 2025 13:34:09 +0900
Subject: [PATCH 105/534] ops: Tweak delay times for bitcoin.crontab
---
production/bitcoin.crontab | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/production/bitcoin.crontab b/production/bitcoin.crontab
index a17147f7b..63df3c52a 100644
--- a/production/bitcoin.crontab
+++ b/production/bitcoin.crontab
@@ -1,13 +1,13 @@
# start test network daemons on boot
-@reboot sleep 5 ; /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
-@reboot sleep 5 ; /usr/local/bin/bitcoind -testnet4 >/dev/null 2>&1
-@reboot sleep 5 ; /usr/local/bin/bitcoind -signet >/dev/null 2>&1
+@reboot sleep 10 ; /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
+@reboot sleep 20 ; /usr/local/bin/bitcoind -testnet4 >/dev/null 2>&1
+@reboot sleep 30 ; /usr/local/bin/bitcoind -signet >/dev/null 2>&1
# start electrs on boot
-@reboot sleep 10 ; screen -dmS mainnet /bitcoin/electrs/start mainnet
-@reboot sleep 10 ; screen -dmS testnet /bitcoin/electrs/start testnet
-@reboot sleep 10 ; screen -dmS testnet4 /bitcoin/electrs/start testnet4
-@reboot sleep 10 ; screen -dmS signet /bitcoin/electrs/start signet
+@reboot sleep 40 ; screen -dmS mainnet /bitcoin/electrs/start mainnet
+@reboot sleep 50 ; screen -dmS testnet /bitcoin/electrs/start testnet
+@reboot sleep 60 ; screen -dmS testnet4 /bitcoin/electrs/start testnet4
+@reboot sleep 70 ; screen -dmS signet /bitcoin/electrs/start signet
# daily update of popular-scripts
30 03 * * * $HOME/electrs/start testnet4 popular-scripts >/dev/null 2>&1
From 322e81d3edcbea536274ea72110188e4f333bc40 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Fri, 14 Mar 2025 14:59:15 +0100
Subject: [PATCH 106/534] Parse tapscript unanimous n-of-n multisig
---
frontend/src/app/shared/script.utils.ts | 52 +++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts
index f0c4701db..8453bdd63 100644
--- a/frontend/src/app/shared/script.utils.ts
+++ b/frontend/src/app/shared/script.utils.ts
@@ -256,6 +256,11 @@ export function detectScriptTemplate(type: ScriptType, script_asm: string, witne
return ScriptTemplates.multisig(tapscriptMultisig.m, tapscriptMultisig.n);
}
+ const tapscriptUnanimousMultisig = parseTapscriptUnanimousMultisig(script_asm);
+ if (tapscriptUnanimousMultisig) {
+ return ScriptTemplates.multisig(tapscriptUnanimousMultisig, tapscriptUnanimousMultisig);
+ }
+
return;
}
@@ -366,6 +371,53 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
return { m, n };
}
+export function parseTapscriptUnanimousMultisig(script: string): undefined | number {
+ if (!script) {
+ return;
+ }
+
+ const ops = script.split(' ');
+ // At minimum, 2 pubkey group (3 tokens) = 6 tokens
+ if (ops.length < 6) {
+ return;
+ }
+
+ if (ops.length % 3 !== 0) {
+ return;
+ }
+
+ const n = ops.length / 3;
+
+ for (let i = 0; i < n; i++) {
+ const pushOp = ops.shift();
+ const pubkey = ops.shift();
+ const sigOp = ops.shift();
+
+ if (pushOp !== 'OP_PUSHBYTES_32') {
+ return;
+ }
+ if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) {
+ return;
+ }
+ if (i < n - 1) {
+ if (sigOp !== 'OP_CHECKSIGVERIFY') {
+ return;
+ }
+ } else {
+ // Last opcode can be either CHECKSIG or CHECKSIGVERIFY
+ if (!(sigOp === 'OP_CHECKSIGVERIFY' || sigOp === 'OP_CHECKSIG')) {
+ return;
+ }
+ }
+ }
+
+ if (ops.length) {
+ return;
+ }
+
+ return n;
+}
+
export function getVarIntLength(n: number): number {
if (n < 0xfd) {
return 1;
From 30003348ce182a53ea5e910bc0f331ea138f7202 Mon Sep 17 00:00:00 2001
From: wiz
Date: Sun, 16 Mar 2025 12:49:26 +0900
Subject: [PATCH 107/534] ops: Fix premature socket close bug in nginx cache
warmer scripts
---
production/nginx-cache-heater | 2 +-
production/nginx-cache-warmer | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/production/nginx-cache-heater b/production/nginx-cache-heater
index 24ec8a061..e6dea270a 100755
--- a/production/nginx-cache-heater
+++ b/production/nginx-cache-heater
@@ -4,7 +4,7 @@ hostname=$(hostname)
heat()
{
echo "$1"
- curl -i -s "$1" | head -1
+ curl -o /dev/null -s "$1"
}
heatURLs=(
diff --git a/production/nginx-cache-warmer b/production/nginx-cache-warmer
index f02091747..171f95430 100755
--- a/production/nginx-cache-warmer
+++ b/production/nginx-cache-warmer
@@ -6,19 +6,19 @@ slugs=(`curl -sSL https://${hostname}/api/v1/mining/pools/3y|jq -r -S '(.pools[]
warmSlurp()
{
echo "$1"
- curl -i -s -H 'User-Agent: Googlebot' "$1" | head -1
+ curl -o /dev/null -s -H 'User-Agent: Googlebot' "$1"
}
warmUnfurl()
{
echo "$1"
- curl -i -s -H 'User-Agent: Twitterbot' "$1" | head -1
+ curl -o /dev/null -s -H 'User-Agent: Twitterbot' "$1"
}
warm()
{
echo "$1"
- curl -i -s "$1" | head -1
+ curl -o /dev/null -s "$1"
}
warmSlurpURLs=(
From 54cf5ea75e8b2c91e18490b02dc1d9c3086fde9b Mon Sep 17 00:00:00 2001
From: softsimon
Date: Tue, 25 Mar 2025 23:04:52 +0700
Subject: [PATCH 108/534] Fix database diabled
---
backend/src/api/blocks.ts | 4 ++--
backend/src/api/common.ts | 7 +++++++
backend/src/api/websocket-handler.ts | 18 +++++++++++++-----
backend/src/index.ts | 4 +++-
4 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts
index 102601594..581850277 100644
--- a/backend/src/api/blocks.ts
+++ b/backend/src/api/blocks.ts
@@ -1391,7 +1391,7 @@ class Blocks {
}
public async $getBlockAuditSummary(hash: string): Promise {
- if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
+ if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && Common.auditIndexingEnabled()) {
return BlocksAuditsRepository.$getBlockAudit(hash);
} else {
return null;
@@ -1399,7 +1399,7 @@ class Blocks {
}
public async $getBlockTxAuditSummary(hash: string, txid: string): Promise {
- if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
+ if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && Common.auditIndexingEnabled()) {
return BlocksAuditsRepository.$getBlockTxAudit(hash, txid);
} else {
return null;
diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts
index 50de63afc..f3569c44c 100644
--- a/backend/src/api/common.ts
+++ b/backend/src/api/common.ts
@@ -722,6 +722,13 @@ export class Common {
);
}
+ static auditIndexingEnabled(): boolean {
+ return (
+ Common.indexingEnabled() &&
+ config.MEMPOOL.AUDIT === true
+ );
+ }
+
static gogglesIndexingEnabled(): boolean {
return (
Common.blocksSummariesIndexingEnabled() &&
diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts
index 390896caa..09e56630a 100644
--- a/backend/src/api/websocket-handler.ts
+++ b/backend/src/api/websocket-handler.ts
@@ -1011,15 +1011,19 @@ class WebsocketHandler {
const blockTransactions = structuredClone(transactions);
this.printLogs();
- await statistics.runStatistics();
+ if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
+ await statistics.runStatistics();
+ }
const _memPool = memPool.getMempool();
const candidateTxs = await memPool.getMempoolCandidates();
let candidates: GbtCandidates | undefined = (memPool.limitGBT && candidateTxs) ? { txs: candidateTxs, added: [], removed: [] } : undefined;
let transactionIds: string[] = (memPool.limitGBT) ? Object.keys(candidates?.txs || {}) : Object.keys(_memPool);
- const accelerations = Object.values(mempool.getAccelerations());
- await accelerationRepository.$indexAccelerationsForBlock(block, accelerations, structuredClone(transactions));
+ if (config.DATABASE.ENABLED) {
+ const accelerations = Object.values(mempool.getAccelerations());
+ await accelerationRepository.$indexAccelerationsForBlock(block, accelerations, structuredClone(transactions));
+ }
const rbfTransactions = Common.findMinedRbfTransactions(transactions, memPool.getSpendMap());
memPool.handleRbfTransactions(rbfTransactions);
@@ -1095,7 +1099,9 @@ class WebsocketHandler {
if (config.CORE_RPC.DEBUG_LOG_PATH && block.extras) {
const firstSeen = getRecentFirstSeen(block.id);
if (firstSeen) {
- BlocksRepository.$saveFirstSeenTime(block.id, firstSeen);
+ if (config.DATABASE.ENABLED) {
+ BlocksRepository.$saveFirstSeenTime(block.id, firstSeen);
+ }
block.extras.firstSeen = firstSeen;
}
}
@@ -1392,7 +1398,9 @@ class WebsocketHandler {
});
}
- await statistics.runStatistics();
+ if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
+ await statistics.runStatistics();
+ }
}
public handleNewStratumJob(job: StratumJob): void {
diff --git a/backend/src/index.ts b/backend/src/index.ts
index dc6a8ae1a..1b2204c28 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -153,7 +153,9 @@ class Server {
await poolsUpdater.updatePoolsJson(); // Needs to be done before loading the disk cache because we sometimes wipe it
await syncAssets.syncAssets$();
- await mempoolBlocks.updatePools$();
+ if (config.DATABASE.ENABLED) {
+ await mempoolBlocks.updatePools$();
+ }
if (config.MEMPOOL.ENABLED) {
if (config.MEMPOOL.CACHE_ENABLED) {
await diskCache.$loadMempoolCache();
From 08194bff96a3db318c448b71f3ade104bd374c67 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 25 Mar 2025 20:14:18 +0100
Subject: [PATCH 109/534] Include tapscript hex in ScriptInfo
---
frontend/src/app/shared/address-utils.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/shared/address-utils.ts b/frontend/src/app/shared/address-utils.ts
index 0a7f2df02..1287fb746 100644
--- a/frontend/src/app/shared/address-utils.ts
+++ b/frontend/src/app/shared/address-utils.ts
@@ -157,8 +157,10 @@ export class AddressTypeInfo {
for (const v of vin) {
if (v.inner_witnessscript_asm) {
this.tapscript = true;
- const controlBlock = v.witness[v.witness.length - 1].startsWith('50') ? v.witness[v.witness.length - 2] : v.witness[v.witness.length - 1];
- this.processScript(new ScriptInfo('inner_witnessscript', undefined, v.inner_witnessscript_asm, v.witness, controlBlock));
+ const hasAnnex = v.witness[v.witness.length - 1].startsWith('50');
+ const controlBlock = hasAnnex ? v.witness[v.witness.length - 2] : v.witness[v.witness.length - 1];
+ const scriptHex = hasAnnex ? v.witness[v.witness.length - 3] : v.witness[v.witness.length - 2];
+ this.processScript(new ScriptInfo('inner_witnessscript', scriptHex, v.inner_witnessscript_asm, v.witness, controlBlock));
}
}
// for single-script types, if we've seen one input we've seen them all
From 3b9c26c706c4c9749a818fa5e79fcc4da1ed1d6a Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 25 Mar 2025 20:16:16 +0100
Subject: [PATCH 110/534] Include input index in ScriptInfo
---
frontend/src/app/components/address/address.component.ts | 2 +-
frontend/src/app/interfaces/electrs.interface.ts | 1 +
frontend/src/app/shared/address-utils.ts | 5 ++++-
frontend/src/app/shared/script.utils.ts | 6 +++++-
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts
index 8786f46ee..62c885bd3 100644
--- a/frontend/src/app/components/address/address.component.ts
+++ b/frontend/src/app/components/address/address.component.ts
@@ -284,7 +284,7 @@ export class AddressComponent implements OnInit, OnDestroy {
let addressVin: Vin[] = [];
for (const tx of this.transactions) {
- addressVin = addressVin.concat(tx.vin.filter(v => v.prevout?.scriptpubkey_address === this.address.address));
+ addressVin = addressVin.concat(tx.vin.map((v, index) => ({ ...v, vinId: `${tx.txid}:${index}` })).filter(v => v.prevout?.scriptpubkey_address === this.address.address));
}
this.addressTypeInfo.processInputs(addressVin);
// hack to trigger change detection
diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts
index aa2a05a2f..d8effcc6f 100644
--- a/frontend/src/app/interfaces/electrs.interface.ts
+++ b/frontend/src/app/interfaces/electrs.interface.ts
@@ -76,6 +76,7 @@ export interface Vin {
issuance?: Issuance;
// Custom
lazy?: boolean;
+ vinId?: string; // `txid:index` where txid links to the transaction this input is spent in, and index is the position of this input within this transaction
// Ord
isInscription?: boolean;
}
diff --git a/frontend/src/app/shared/address-utils.ts b/frontend/src/app/shared/address-utils.ts
index 1287fb746..e753faac5 100644
--- a/frontend/src/app/shared/address-utils.ts
+++ b/frontend/src/app/shared/address-utils.ts
@@ -160,7 +160,7 @@ export class AddressTypeInfo {
const hasAnnex = v.witness[v.witness.length - 1].startsWith('50');
const controlBlock = hasAnnex ? v.witness[v.witness.length - 2] : v.witness[v.witness.length - 1];
const scriptHex = hasAnnex ? v.witness[v.witness.length - 3] : v.witness[v.witness.length - 2];
- this.processScript(new ScriptInfo('inner_witnessscript', scriptHex, v.inner_witnessscript_asm, v.witness, controlBlock));
+ this.processScript(new ScriptInfo('inner_witnessscript', scriptHex, v.inner_witnessscript_asm, v.witness, controlBlock, v.vinId));
}
}
// for single-script types, if we've seen one input we've seen them all
@@ -214,6 +214,9 @@ export class AddressTypeInfo {
}
private processScript(script: ScriptInfo): void {
+ if (this.scripts.has(script.key)) {
+ return;
+ }
this.scripts.set(script.key, script);
if (script.template?.type === 'multisig') {
this.isMultisig = { m: script.template['m'], n: script.template['n'] };
diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts
index 62a7a5845..26a1eade5 100644
--- a/frontend/src/app/shared/script.utils.ts
+++ b/frontend/src/app/shared/script.utils.ts
@@ -174,15 +174,19 @@ export class ScriptInfo {
scriptPath?: string;
hex?: string;
asm?: string;
+ vinId?: string;
template: ScriptTemplate;
- constructor(type: ScriptType, hex?: string, asm?: string, witness?: string[], scriptPath?: string) {
+ constructor(type: ScriptType, hex?: string, asm?: string, witness?: string[], scriptPath?: string, vinId?: string) {
this.type = type;
this.hex = hex;
this.asm = asm;
if (scriptPath) {
this.scriptPath = scriptPath;
}
+ if (vinId) {
+ this.vinId = vinId;
+ }
if (this.asm) {
this.template = detectScriptTemplate(this.type, this.asm, witness);
}
From 92fda6b8c11cf808fdb215e89b2125399cd637ff Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 25 Mar 2025 20:17:04 +0100
Subject: [PATCH 111/534] Allow to crop custom length in asm styler
---
.../transactions-list/transactions-list.component.html | 2 +-
frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html
index 6f1d76538..e8646279b 100644
--- a/frontend/src/app/components/transactions-list/transactions-list.component.html
+++ b/frontend/src/app/components/transactions-list/transactions-list.component.html
@@ -164,7 +164,7 @@
P2WSH witness script
-
+
1000" style="display: flex;">
...
diff --git a/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts b/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts
index f152f2f54..ab5d9b227 100644
--- a/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts
+++ b/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts
@@ -5,7 +5,7 @@ import { Pipe, PipeTransform } from '@angular/core';
})
export class AsmStylerPipe implements PipeTransform {
- transform(asm: string, showAll = true): string {
+ transform(asm: string, crop: number = 0): string {
const instructions = asm.split('OP_');
let out = '';
let chars = -3;
@@ -13,7 +13,7 @@ export class AsmStylerPipe implements PipeTransform {
if (instruction === '') {
continue;
}
- if (!showAll && chars > 1000) {
+ if (crop && chars > crop) {
break;
}
chars += instruction.length + 3;
From 2945e47eba9583cf2826d2ca9f159f9707eec563 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 26 Mar 2025 23:05:43 +0100
Subject: [PATCH 112/534] [blocks] respect 404 error code instead of misleading
500
---
backend/src/api/bitcoin/bitcoin.routes.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts
index 73a14ba4e..3cf2923f1 100644
--- a/backend/src/api/bitcoin/bitcoin.routes.ts
+++ b/backend/src/api/bitcoin/bitcoin.routes.ts
@@ -406,8 +406,8 @@ class BitcoinRoutes {
res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString());
res.json(block);
- } catch (e) {
- handleError(req, res, 500, 'Failed to get block');
+ } catch (e: any) {
+ handleError(req, res, e?.response?.status === 404 ? 404 : 500, 'Failed to get block');
}
}
From 7369f55fab0aabba57b805e49eca7c65bdc7a068 Mon Sep 17 00:00:00 2001
From: natsoni
Date: Tue, 25 Mar 2025 20:17:52 +0100
Subject: [PATCH 113/534] Taptree widget
---
.../components/address/address.component.html | 16 +-
.../components/address/address.component.ts | 2 +
.../taproot-address-scripts.component.html | 15 +
.../taproot-address-scripts.component.scss | 31 ++
.../taproot-address-scripts.component.ts | 362 ++++++++++++++++++
frontend/src/app/graphs/echarts.ts | 4 +-
frontend/src/app/graphs/graphs.module.ts | 6 +
frontend/src/app/shared/transaction.utils.ts | 32 +-
8 files changed, 462 insertions(+), 6 deletions(-)
create mode 100644 frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.html
create mode 100644 frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.scss
create mode 100644 frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.ts
diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html
index 41d8c151f..5f6f0545a 100644
--- a/frontend/src/app/components/address/address.component.html
+++ b/frontend/src/app/components/address/address.component.html
@@ -73,6 +73,20 @@
+
+
+
+
Taproot Tree
+
+
+
+
2">
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts
index 62c885bd3..a4fc72f16 100644
--- a/frontend/src/app/components/address/address.component.ts
+++ b/frontend/src/app/components/address/address.component.ts
@@ -115,6 +115,7 @@ export class AddressComponent implements OnInit, OnDestroy {
addressLoadingStatus$: Observable;
addressInfo: null | AddressInformation = null;
addressTypeInfo: null | AddressTypeInfo;
+ hasTapTree: boolean;
fullyLoaded = false;
chainStats: AddressStats;
@@ -287,6 +288,7 @@ export class AddressComponent implements OnInit, OnDestroy {
addressVin = addressVin.concat(tx.vin.map((v, index) => ({ ...v, vinId: `${tx.txid}:${index}` })).filter(v => v.prevout?.scriptpubkey_address === this.address.address));
}
this.addressTypeInfo.processInputs(addressVin);
+ this.hasTapTree = this.addressTypeInfo.tapscript && this.addressTypeInfo.scripts.values().next().value.scriptPath.length / 2 > 33;
// hack to trigger change detection
this.addressTypeInfo = this.addressTypeInfo.clone();
diff --git a/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.html b/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.html
new file mode 100644
index 000000000..741f2e1d7
--- /dev/null
+++ b/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.html
@@ -0,0 +1,15 @@
+
+ maxHeight">
+ maxHeight || fullTreeShown)">
+
+ Show all
+
+
+ Show less
+
+
diff --git a/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.scss b/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.scss
new file mode 100644
index 000000000..3d7e30884
--- /dev/null
+++ b/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.scss
@@ -0,0 +1,31 @@
+.fade-out {
+ position: relative;
+
+ &::before {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 40px;
+ top: -40px;
+ background: linear-gradient(to top,
+ var(--fade-out-box-bg-end) 0%,
+ var(--fade-out-box-bg-end) 30%,
+ var(--fade-out-box-bg-start) 100%);
+ z-index: 10000000;
+ }
+}
+
+.toggle-wrapper {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+}
+
+.button-container {
+ display: flex;
+ justify-content: center;
+}
+
+.graph-toggle {
+ margin-top: 10px;
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.ts b/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.ts
new file mode 100644
index 000000000..6a0ed575c
--- /dev/null
+++ b/frontend/src/app/components/taproot-address-scripts/taproot-address-scripts.component.ts
@@ -0,0 +1,362 @@
+import { Component, ChangeDetectionStrategy, Input, OnChanges, NgZone, SimpleChanges } from '@angular/core';
+import { Router } from '@angular/router';
+import { Location } from '@angular/common';
+import { AddressTypeInfo } from '@app/shared/address-utils';
+import { EChartsOption } from '@app/graphs/echarts';
+import { ScriptInfo } from '@app/shared/script.utils';
+import { compactSize, taggedHash, uint8ArrayToHexString } from '@app/shared/transaction.utils';
+import { StateService } from '@app/services/state.service';
+import { AsmStylerPipe } from '@app/shared/pipes/asm-styler/asm-styler.pipe';
+import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
+
+interface TaprootTree {
+ name: string; // the TapBranch hash or TapLeaf script hash
+ value?: {
+ leafVersion: number;
+ script: ScriptInfo;
+ };
+ depth?: number;
+ children?: [TaprootTree, TaprootTree];
+ // ECharts properties
+ symbol?: string;
+ symbolSize?: number;
+ symbolOffset?: number[];
+ label?: any;
+ tooltip?: { label: string, content?: string }[];
+}
+
+@Component({
+ selector: 'app-taproot-address-scripts',
+ templateUrl: './taproot-address-scripts.component.html',
+ styleUrls: ['./taproot-address-scripts.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class TaprootAddressScriptsComponent implements OnChanges {
+ @Input() address: AddressTypeInfo;
+
+ tree: TaprootTree;
+ depth: number = 0;
+ height: number;
+ fullTreeShown: boolean;
+ maxHeight: number = 400;
+
+ chartOptions: EChartsOption = {};
+ chartInitOptions = {
+ renderer: 'svg',
+ };
+ chartInstance: any;
+ isTouchscreen: boolean = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || (navigator as any).msMaxTouchPoints > 0;
+
+ constructor(
+ public stateService: StateService,
+ private asmStylerPipe: AsmStylerPipe,
+ private location: Location,
+ private relativeUrlPipe: RelativeUrlPipe,
+ private router: Router,
+ private zone: NgZone,
+ ) { }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (changes.address?.currentValue.scripts) {
+ this.buildTree();
+ this.prepareTree(this.tree, 0);
+ this.prepareChartOptions();
+ }
+ }
+
+ buildTree(): void {
+ if (this.address?.scripts.size) {
+ this.tree = { name: '' };
+ for (const script of this.address.scripts.values()) {
+ let { leafVersion, merklePath } = this.parseControlBlock(script.scriptPath);
+ this.tree = this.addPathToTree(this.tree, script, leafVersion, merklePath);
+ }
+ this.height = (this.depth + 1) * 40;
+ }
+ }
+
+ parseControlBlock(controlBlock: string): { leafVersion: number, merklePath: string[] } {
+
+ const m = ((controlBlock.length / 2) - 33) / 32;
+ if (!Number.isInteger(m)) {
+ throw new Error("Invalid scriptPath: length does not match the expected format.");
+ }
+
+ const leafVersion = parseInt(controlBlock.slice(0, 2), 16) & 0xfe;
+ const merklePath = [];
+ for (let i = 0; i < m; i++) {
+ merklePath.push(controlBlock.slice(66 + i * 64, 66 + (i + 1) * 64));
+ }
+
+ if (merklePath.length > this.depth) {
+ this.depth = merklePath.length;
+ }
+
+ return { leafVersion, merklePath };
+ }
+
+ addPathToTree(masterTree: TaprootTree, script: ScriptInfo, leafVersion: number, merklePath: string[]): TaprootTree {
+ // See https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
+ let k = taggedHash('TapLeaf', leafVersion.toString(16) + uint8ArrayToHexString(compactSize(script.hex.length / 2)) + script.hex);
+ let node: TaprootTree = { name: k, value: { leafVersion, script } };
+
+ // Start from the leaf and go up until we can merge in the current tree
+ for (let i = 0; i < merklePath.length; i++) {
+ const e = merklePath[i];
+ const [left, right] = [k, e].sort((a, b) => a.localeCompare(b));
+ const parentHash = taggedHash('TapBranch', left + right);
+ const isFirstChild = left === k;
+ const children: [TaprootTree, TaprootTree] = isFirstChild ? [node, { name: e }] : [{ name: e }, node];
+
+ if (this.mergeBranchAtDepth(masterTree, parentHash, children, isFirstChild, merklePath.length - i - 1)) {
+ return masterTree;
+ }
+
+ k = parentHash;
+ node = { name: k, children };
+
+ }
+
+ return node;
+ }
+
+ mergeBranchAtDepth(tree: TaprootTree, target: string, children: [TaprootTree, TaprootTree], first: boolean, targetDepth: number, currentDepth = 0): boolean {
+ if (!tree) {
+ return false;
+ }
+
+ if (currentDepth === targetDepth) {
+ if (tree.name === target) {
+ if (!tree.children) {
+ tree.children = children;
+ } else {
+ if (first) {
+ tree.children[0] = children[0];
+ } else {
+ tree.children[1] = children[1];
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ if (tree.children) {
+ for (const child of tree.children) {
+ if (this.mergeBranchAtDepth(child, target, children, first, targetDepth, currentDepth + 1)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ prepareTree(node: TaprootTree, depth: number): void {
+ if (!node) {
+ return;
+ }
+
+ node.depth = depth;
+ node.symbol = 'none';
+
+ const basePillStyle = {
+ align: 'center',
+ padding: [3, 6],
+ borderRadius: 10,
+ fontSize: 10,
+ fontWeight: 'bold',
+ fontFamily: 'system-ui',
+ };
+
+ if (depth === 0) {
+ node.symbol = 'none';
+ node.label = {
+ formatter: '{pill|TapRoot}',
+ offset: [0, -5],
+ rich: {
+ pill: {
+ ...basePillStyle,
+ backgroundColor: 'var(--tertiary)',
+ color: '#fff',
+ },
+ },
+ };
+ node.tooltip = [
+ { label: 'TapRoot Hash', content: node.name.slice(0, 10) + '…' + node.name.slice(-10) },
+ ];
+ }
+
+ if (node.children) {
+ if (depth > 0) {
+ node.symbol = 'circle';
+ node.symbolSize = 10;
+ node.symbolOffset = [0, 5];
+ node.label = { formatter: '' };
+ node.tooltip = [
+ { label: 'TapBranch Hash', content: node.name.slice(0, 10) + '…' + node.name.slice(-10) },
+ { label: 'Depth', content: depth.toString() },
+ ];
+ }
+ this.prepareTree(node.children[0], depth + 1);
+ this.prepareTree(node.children[1], depth + 1);
+ } else {
+ if (node.value) {
+ const script = node.value.script;
+ const label = script.template?.label;
+
+ node.label = {
+ formatter: `{pill|${label || 'Script'}}`,
+ offset: [0, 5],
+ verticalAlign: 'middle',
+ rich: {
+ pill: {
+ ...basePillStyle,
+ backgroundColor: '#ffc107',
+ color: '#212529'
+ }
+ }
+ };
+
+ node.tooltip = [
+ { label: 'TapLeaf Hash', content: node.name.slice(0, 10) + '…' + node.name.slice(-10) },
+ { label: 'Depth', content: depth.toString() },
+ { label: 'Leaf Version', content: node.value.leafVersion.toString(16) },
+ ];
+
+ } else {
+ node.symbol = 'circle';
+ node.symbolSize = 10;
+ node.symbolOffset = [0, 5];
+ node.label = { formatter: '' };
+ node.tooltip = [
+ { label: 'Hash', content: node.name.slice(0, 10) + '…' + node.name.slice(-10) },
+ { label: 'Depth', content: depth.toString() },
+ ];
+ }
+ }
+ }
+
+ prepareChartOptions() {
+ if (!this.tree) {
+ return;
+ }
+
+ this.chartOptions = {
+ tooltip: {
+ show: true,
+ backgroundColor: 'rgba(17, 19, 31, 1)',
+ borderRadius: 4,
+ shadowColor: 'rgba(0, 0, 0, 0.5)',
+ confine: true,
+ textStyle: {
+ color: '#b1b1b1',
+ },
+ borderColor: '#000',
+ formatter: (params: any) => {
+ const node: TaprootTree = params.data;
+ if (!node.tooltip) {
+ return '';
+ }
+
+ let rows = node.tooltip.map(
+ (item) =>
+ `
+ ${item.label}
+ ${item.content}
+ `
+ ).join('');
+
+ if (node.value?.script.vinId) {
+ const [txid, vinIndex] = node.value.script.vinId.split(':');
+ rows += `
+
+ Last used in tx
+
+ ${txid.slice(0, 10) + '…' + txid.slice(-10)}
+
+ `;
+ }
+
+ let asmContent = '';
+ if (node.value?.script?.asm) {
+ const asm = this.asmStylerPipe.transform(node.value.script.asm, 300);
+ asmContent = `
+
+
${asm} ${node.value.script.asm.length > 300 ? '...' : ''}
+ `;
+ }
+
+ let hiddenScriptsMessage = '';
+ if (node.tooltip[0].label === 'Hash') {
+ hiddenScriptsMessage = `
+
+ This node might commit to one or more scripts that have not been revealed yet.
+
`;
+ }
+
+ return `
+
+
+ ${asmContent}
+ ${hiddenScriptsMessage}
+
`;
+ },
+ },
+ series: [{
+ type: 'tree',
+ data: [this.tree as any],
+ top: '20',
+ bottom: '20',
+ right: 0,
+ left: 0,
+ lineStyle: {
+ curveness: 0.9,
+ width: 2,
+ },
+ emphasis: {
+ focus: 'ancestor',
+ itemStyle: {
+ color: '#ccc',
+ },
+ lineStyle: {
+ color: '#ccc',
+ }
+ },
+ orient: 'TB',
+ expandAndCollapse: false,
+ animationDurationUpdate: 0,
+ animationDuration: 0,
+ }],
+ };
+ }
+
+ onChartInit(ec) {
+ this.chartInstance = ec;
+ this.chartInstance.on('click', 'series', this.onChartClick.bind(this));
+ }
+
+ onChartClick(e): void {
+ if (this.isTouchscreen) { // show tooltip on touchscreen, and click on link in tooltip to navigate
+ return;
+ }
+
+ if (!e.data.value?.script.vinId) {
+ return;
+ }
+
+ const [txid, vinIndex] = e.data.value.script.vinId.split(':');
+ const url = this.router.createUrlTree([this.relativeUrlPipe.transform('/tx'), txid], { fragment: 'vin=' + vinIndex });
+
+ this.zone.run(() => {
+ if (e.event?.event?.ctrlKey || e.event?.event?.metaKey) {
+ const fullUrl = this.location.prepareExternalUrl(this.router.serializeUrl(url));
+ window.open(fullUrl, '_blank');
+ } else {
+ this.router.navigate([this.relativeUrlPipe.transform('/tx'), txid], { fragment: 'vin=' + vinIndex });
+ }
+ });
+
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/graphs/echarts.ts b/frontend/src/app/graphs/echarts.ts
index 67ed7e3b8..7fd309c24 100644
--- a/frontend/src/app/graphs/echarts.ts
+++ b/frontend/src/app/graphs/echarts.ts
@@ -1,6 +1,6 @@
// Import tree-shakeable echarts
import * as echarts from 'echarts/core';
-import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart, CustomChart } from 'echarts/charts';
+import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart, CustomChart, TreeChart } from 'echarts/charts';
import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent } from 'echarts/components';
import { SVGRenderer, CanvasRenderer } from 'echarts/renderers';
// Typescript interfaces
@@ -13,6 +13,6 @@ echarts.use([
LegendComponent, GeoComponent, DataZoomComponent,
VisualMapComponent, MarkLineComponent,
LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart,
- CustomChart,
+ CustomChart, TreeChart
]);
export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption };
\ No newline at end of file
diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts
index f882b4221..9be556424 100644
--- a/frontend/src/app/graphs/graphs.module.ts
+++ b/frontend/src/app/graphs/graphs.module.ts
@@ -41,7 +41,9 @@ import { AddressGraphComponent } from '@components/address-graph/address-graph.c
import { UtxoGraphComponent } from '@components/utxo-graph/utxo-graph.component';
import { ActiveAccelerationBox } from '@components/acceleration/active-acceleration-box/active-acceleration-box.component';
import { AddressesTreemap } from '@components/addresses-treemap/addresses-treemap.component';
+import { TaprootAddressScriptsComponent } from '@components/taproot-address-scripts/taproot-address-scripts.component';
import { CommonModule } from '@angular/common';
+import { AsmStylerPipe } from '@app/shared/pipes/asm-styler/asm-styler.pipe';
@NgModule({
declarations: [
@@ -85,6 +87,7 @@ import { CommonModule } from '@angular/common';
UtxoGraphComponent,
ActiveAccelerationBox,
AddressesTreemap,
+ TaprootAddressScriptsComponent,
],
imports: [
CommonModule,
@@ -97,6 +100,9 @@ import { CommonModule } from '@angular/common';
exports: [
NgxEchartsModule,
ActiveAccelerationBox,
+ ],
+ providers: [
+ AsmStylerPipe
]
})
export class GraphsModule { }
diff --git a/frontend/src/app/shared/transaction.utils.ts b/frontend/src/app/shared/transaction.utils.ts
index eafe8ae99..d990d182d 100644
--- a/frontend/src/app/shared/transaction.utils.ts
+++ b/frontend/src/app/shared/transaction.utils.ts
@@ -3,7 +3,7 @@ import { getVarIntLength, parseMultisigScript, isPoint } from '@app/shared/scrip
import { Transaction, Vin } from '@interfaces/electrs.interface';
import { CpfpInfo, RbfInfo, TransactionStripped } from '@interfaces/node-api.interface';
import { StateService } from '@app/services/state.service';
-import { Hash } from './sha256';
+import { hash, Hash } from './sha256';
// Bitcoin Core default policy settings
const MAX_STANDARD_TX_WEIGHT = 400_000;
@@ -1380,11 +1380,11 @@ function toWords(bytes) {
}
// Helper functions
-function uint8ArrayToHexString(uint8Array: Uint8Array): string {
+export function uint8ArrayToHexString(uint8Array: Uint8Array): string {
return Array.from(uint8Array).map(byte => byte.toString(16).padStart(2, '0')).join('');
}
-function hexStringToUint8Array(hex: string): Uint8Array {
+export function hexStringToUint8Array(hex: string): Uint8Array {
const buf = new Uint8Array(hex.length / 2);
for (let i = 0; i < buf.length; i++) {
buf[i] = parseInt(hex.substr(i * 2, 2), 16);
@@ -1521,6 +1521,32 @@ function readVector(buffer: Uint8Array, offset: number): [Uint8Array[], number]
return [vector, updatedOffset];
}
+// SHA256(SHA256(tag) || SHA256(tag) || dataHex)
+export function taggedHash(tag: string, dataHex: string): string {
+ const encoder = new TextEncoder();
+ const tagHash = hash(encoder.encode(tag));
+ return uint8ArrayToHexString(hash(new Uint8Array([...tagHash, ...tagHash, ...hexStringToUint8Array(dataHex)])));
+}
+
+export function compactSize(n: number): Uint8Array {
+ if (n <= 252) {
+ return new Uint8Array([n]);
+ } else if (n <= 0xffff) {
+ return new Uint8Array([0xfd, n & 0xff, (n >> 8) & 0xff]);
+ } else if (n <= 0xffffffff) {
+ return new Uint8Array([0xfe, n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]);
+ } else {
+ const buffer = new Uint8Array(9);
+ buffer[0] = 0xff;
+ let num = BigInt(n);
+ for (let i = 1; i <= 8; i++) {
+ buffer[i] = Number(num & BigInt(0xff));
+ num >>= BigInt(8);
+ }
+ return buffer;
+ }
+}
+
// Inversed the opcodes object from https://github.com/mempool/mempool/blob/14e49126c3ca8416a8d7ad134a95c5e090324d69/backend/src/utils/bitcoin-script.ts#L1
const opcodes = {
0: 'OP_0',
From b153d21162aea4e51ee773fe96bf24137a14bf0b Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Mon, 10 Mar 2025 04:35:52 +0000
Subject: [PATCH 114/534] automatically fetch enabled wallets from services
backend
---
backend/src/api/services/wallets.ts | 33 ++++++++++++++++++++++++++
backend/src/config.ts | 2 ++
production/mempool-config.mainnet.json | 1 +
3 files changed, 36 insertions(+)
diff --git a/backend/src/api/services/wallets.ts b/backend/src/api/services/wallets.ts
index dd4d7ebc9..f498a80ad 100644
--- a/backend/src/api/services/wallets.ts
+++ b/backend/src/api/services/wallets.ts
@@ -30,6 +30,7 @@ const POLL_FREQUENCY = 5 * 60 * 1000; // 5 minutes
class WalletApi {
private wallets: Record = {};
private syncing = false;
+ private lastSync = 0;
constructor() {
this.wallets = config.WALLETS.ENABLED ? (config.WALLETS.WALLETS as string[]).reduce((acc, wallet) => {
@@ -47,7 +48,38 @@ class WalletApi {
if (!config.WALLETS.ENABLED || this.syncing) {
return;
}
+
this.syncing = true;
+
+ if (config.WALLETS.AUTO && (Date.now() - this.lastSync) > POLL_FREQUENCY) {
+ try {
+ // update list of active wallets
+ this.lastSync = Date.now();
+ const response = await axios.get(config.MEMPOOL_SERVICES.API + `/wallets`);
+ const walletList: string[] = response.data;
+ if (walletList) {
+ // create a quick lookup dictionary of active wallets
+ const newWallets: Record = Object.fromEntries(
+ walletList.map(wallet => [wallet, true])
+ );
+ for (const wallet of walletList) {
+ // don't overwrite existing wallets
+ if (!this.wallets[wallet]) {
+ this.wallets[wallet] = { name: wallet, addresses: {}, lastPoll: 0 };
+ }
+ }
+ // remove wallets that are no longer active
+ for (const wallet of Object.keys(this.wallets)) {
+ if (!newWallets[wallet]) {
+ delete this.wallets[wallet];
+ }
+ }
+ }
+ } catch (e) {
+ logger.err(`Error updating active wallets: ${(e instanceof Error ? e.message : e)}`);
+ }
+ }
+
for (const walletKey of Object.keys(this.wallets)) {
const wallet = this.wallets[walletKey];
if (wallet.lastPoll < (Date.now() - POLL_FREQUENCY)) {
@@ -72,6 +104,7 @@ class WalletApi {
}
}
}
+
this.syncing = false;
}
diff --git a/backend/src/config.ts b/backend/src/config.ts
index a1050a7d5..3fe3db2ee 100644
--- a/backend/src/config.ts
+++ b/backend/src/config.ts
@@ -164,6 +164,7 @@ interface IConfig {
},
WALLETS: {
ENABLED: boolean;
+ AUTO: boolean;
WALLETS: string[];
},
STRATUM: {
@@ -334,6 +335,7 @@ const defaults: IConfig = {
},
'WALLETS': {
'ENABLED': false,
+ 'AUTO': false,
'WALLETS': [],
},
'STRATUM': {
diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json
index 9505601d2..87f58f916 100644
--- a/production/mempool-config.mainnet.json
+++ b/production/mempool-config.mainnet.json
@@ -159,6 +159,7 @@
},
"WALLETS": {
"ENABLED": true,
+ "AUTO": true,
"WALLETS": ["BITB", "3350"]
},
"STRATUM": {
From 072b83243e5202de0b2722855d8f66ca22031f18 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Mon, 10 Mar 2025 04:38:51 +0000
Subject: [PATCH 115/534] update custom dashboard config
---
frontend/custom-sv-config.json | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/frontend/custom-sv-config.json b/frontend/custom-sv-config.json
index dee3dab18..9a61704a2 100644
--- a/frontend/custom-sv-config.json
+++ b/frontend/custom-sv-config.json
@@ -16,10 +16,10 @@
"mobileOrder": 4
},
{
- "component": "balance",
+ "component": "walletBalance",
"mobileOrder": 1,
"props": {
- "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo"
+ "wallet": "ONBTC"
}
},
{
@@ -30,21 +30,22 @@
}
},
{
- "component": "address",
+ "component": "wallet",
"mobileOrder": 2,
"props": {
- "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo",
- "period": "1m"
+ "wallet": "ONBTC",
+ "period": "1m",
+ "label": "bitcoin.gob.sv"
}
},
{
"component": "blocks"
},
{
- "component": "addressTransactions",
+ "component": "walletTransactions",
"mobileOrder": 3,
"props": {
- "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo"
+ "wallet": "ONBTC"
}
}
]
From 3056454389ea80380920ef8e702089e9bb629f55 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Wed, 26 Feb 2025 04:48:22 +0000
Subject: [PATCH 116/534] Add configurable label to balance widget
---
.../address-graph/address-graph.component.ts | 22 +++++++++++++++++++
.../custom-dashboard.component.html | 4 ++--
frontend/src/app/graphs/echarts.ts | 4 ++--
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/frontend/src/app/components/address-graph/address-graph.component.ts b/frontend/src/app/components/address-graph/address-graph.component.ts
index 2bbfd5e34..005c64e9f 100644
--- a/frontend/src/app/components/address-graph/address-graph.component.ts
+++ b/frontend/src/app/components/address-graph/address-graph.component.ts
@@ -44,6 +44,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
@Input() right: number | string = 10;
@Input() left: number | string = 70;
@Input() widget: boolean = false;
+ @Input() label: string = '';
@Input() defaultFiat: boolean = false;
@Input() showLegend: boolean = true;
@Input() showYAxis: boolean = true;
@@ -55,6 +56,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
hoverData: any[] = [];
conversions: any;
allowZoom: boolean = false;
+ labelGraphic: any;
selected = { [$localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`]: true, 'Fiat': false };
@@ -85,6 +87,18 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
ngOnChanges(changes: SimpleChanges): void {
this.isLoading = true;
+ this.labelGraphic = this.label ? {
+ type: 'text',
+ right: '36px',
+ bottom: '36px',
+ z: 100,
+ silent: true,
+ style: {
+ fill: '#fff',
+ text: this.label,
+ font: '24px sans-serif'
+ }
+ } : undefined;
if (!this.addressSummary$ && (!this.address || !this.stats)) {
return;
}
@@ -205,6 +219,10 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
right: this.adjustedRight,
left: this.adjustedLeft,
},
+ graphic: this.labelGraphic ? [{
+ ...this.labelGraphic,
+ right: this.adjustedRight + 22 + 'px',
+ }] : undefined,
legend: (this.showLegend && !this.stateService.isAnyTestnet()) ? {
data: [
{
@@ -443,6 +461,10 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
right: this.adjustedRight,
left: this.adjustedLeft,
},
+ graphic: this.labelGraphic ? [{
+ ...this.labelGraphic,
+ right: this.adjustedRight + 22 + 'px',
+ }] : undefined,
legend: {
selected: this.selected,
},
diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
index 8ca1a5ac4..defb7c068 100644
--- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
+++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
@@ -238,7 +238,7 @@
-
+
@@ -272,7 +272,7 @@
-
+
diff --git a/frontend/src/app/graphs/echarts.ts b/frontend/src/app/graphs/echarts.ts
index 67ed7e3b8..36a0517e4 100644
--- a/frontend/src/app/graphs/echarts.ts
+++ b/frontend/src/app/graphs/echarts.ts
@@ -1,7 +1,7 @@
// Import tree-shakeable echarts
import * as echarts from 'echarts/core';
import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart, CustomChart } from 'echarts/charts';
-import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent } from 'echarts/components';
+import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent, GraphicComponent } from 'echarts/components';
import { SVGRenderer, CanvasRenderer } from 'echarts/renderers';
// Typescript interfaces
import { EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts';
@@ -13,6 +13,6 @@ echarts.use([
LegendComponent, GeoComponent, DataZoomComponent,
VisualMapComponent, MarkLineComponent,
LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart,
- CustomChart,
+ CustomChart, GraphicComponent
]);
export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption };
\ No newline at end of file
From fb50ea7a6d06e3a4a28db3061d8b2ba74bedafee Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Tue, 4 Feb 2025 12:02:12 +0000
Subject: [PATCH 117/534] detect and warn about address poisoning attacks
---
.../transactions-list.component.html | 21 ++-
.../transactions-list.component.ts | 56 +++++++
frontend/src/app/shared/address-utils.ts | 145 ++++++++++++++++++
.../address-text/address-text.component.html | 17 ++
.../address-text/address-text.component.scss | 32 ++++
.../address-text/address-text.component.ts | 20 +++
frontend/src/app/shared/shared.module.ts | 6 +-
7 files changed, 290 insertions(+), 7 deletions(-)
create mode 100644 frontend/src/app/shared/components/address-text/address-text.component.html
create mode 100644 frontend/src/app/shared/components/address-text/address-text.component.scss
create mode 100644 frontend/src/app/shared/components/address-text/address-text.component.ts
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html
index 6f1d76538..7721298fb 100644
--- a/frontend/src/app/components/transactions-list/transactions-list.component.html
+++ b/frontend/src/app/components/transactions-list/transactions-list.component.html
@@ -16,6 +16,11 @@