From ce950d63cb4fcb9f2a5c530753f2599b4e7f0588 Mon Sep 17 00:00:00 2001
From: hunicus <93150691+hunicus@users.noreply.github.com>
Date: Mon, 12 Jun 2023 07:07:15 -0400
Subject: [PATCH 01/53] Replace disclaimer text regarding tx acceleration
---
frontend/src/app/docs/api-docs/api-docs.component.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 392cda19e..b788d2739 100644
--- a/frontend/src/app/docs/api-docs/api-docs.component.html
+++ b/frontend/src/app/docs/api-docs/api-docs.component.html
@@ -10,8 +10,8 @@
-
| mempool.space merely provides data about the Bitcoin network. It cannot help you with retrieving funds, confirming your transaction quicker, etc. For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc). |
-
mempool.space merely provides data about the Bitcoin network. It cannot help you with retrieving funds, confirming your transaction quicker, etc.
For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc).
+
| mempool.space merely provides data about the Bitcoin network. It cannot help you with retrieving funds, wallet issues, etc. For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc). |
+
mempool.space merely provides data about the Bitcoin network. It cannot help you with retrieving funds, wallet issues, etc.
For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc).
From c89b15fdbc4294a645f96854b9d50d895db2451c Mon Sep 17 00:00:00 2001
From: hunicus <93150691+hunicus@users.noreply.github.com>
Date: Tue, 27 Jun 2023 10:24:31 -0400
Subject: [PATCH 02/53] Update mosp strings from tm to r
---
LICENSE | 2 +-
README.md | 2 +-
frontend/src/app/components/about/about.component.html | 2 +-
.../components/privacy-policy/privacy-policy.component.html | 2 +-
.../trademark-policy/trademark-policy.component.html | 4 ++--
.../components/global-footer/global-footer.component.html | 4 ++--
frontend/src/index.bisq.html | 4 ++--
frontend/src/index.liquid.html | 4 ++--
frontend/src/index.mempool.html | 4 ++--
unfurler/src/index.ts | 4 ++--
10 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/LICENSE b/LICENSE
index ac267d120..9f8592854 100644
--- a/LICENSE
+++ b/LICENSE
@@ -13,7 +13,7 @@ the terms of (at your option) either:
proxy statement published on
.
However, this copyright license does not include an implied right or license to
-use our trademarks: The Mempool Open Source Project™, mempool.space™, the
+use our trademarks: The Mempool Open Source Project®, mempool.space™, the
mempool Logo™, the mempool.space Vertical Logo™, the mempool.space Horizontal
Logo™, the mempool Square Logo™, and the mempool Blocks logo™ are registered
trademarks or trademarks of Mempool Space K.K in Japan, the United States,
diff --git a/README.md b/README.md
index b7a455cd5..dd2e62478 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# The Mempool Open Source Project™ [](https://dashboard.cypress.io/projects/ry4br7/runs)
+# The Mempool Open Source Project® [](https://dashboard.cypress.io/projects/ry4br7/runs)
https://user-images.githubusercontent.com/93150691/226236121-375ea64f-b4a1-4cc0-8fad-a6fb33226840.mp4
diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html
index 9b2bdf6c0..4100273dd 100644
--- a/frontend/src/app/components/about/about.component.html
+++ b/frontend/src/app/components/about/about.component.html
@@ -411,7 +411,7 @@
Trademark Notice
- The Mempool Open Source Project™, mempool.space™, the mempool logo®, the mempool.space logos™, the mempool square logo®, and the mempool blocks 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.space™, the mempool logo®, the mempool.space logos™, the mempool square logo®, and the mempool blocks 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/privacy-policy/privacy-policy.component.html b/frontend/src/app/components/privacy-policy/privacy-policy.component.html
index fc93b9aa9..e0979ce24 100644
--- a/frontend/src/app/components/privacy-policy/privacy-policy.component.html
+++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.html
@@ -43,7 +43,7 @@
TRUST YOUR OWN SELF-HOSTED MEMPOOL EXPLORER
- For maximum privacy, we recommend that you use your own self-hosted instance of The Mempool Open Source Project™ on your own hardware. You can easily install your own self-hosted instance of this website on a Raspberry Pi using a one-click installation method maintained by various Bitcoin fullnode distributions such as Umbrel, RaspiBlitz, MyNode, and RoninDojo. See our project's GitHub page for more details about self-hosting this website.
+ For maximum privacy, we recommend that you use your own self-hosted instance of The Mempool Open Source Project® on your own hardware. You can easily install your own self-hosted instance of this website on a Raspberry Pi using a one-click installation method maintained by various Bitcoin fullnode distributions such as Umbrel, RaspiBlitz, MyNode, and RoninDojo. See our project's GitHub page for more details about self-hosting this website.
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 4f9419642..3a3da15dd 100644
--- a/frontend/src/app/components/trademark-policy/trademark-policy.component.html
+++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.html
@@ -7,7 +7,7 @@
Trademark Policy and Guidelines
-
The Mempool Open Source Project ™
+
The Mempool Open Source Project ®
Updated: July 19, 2021
@@ -304,7 +304,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 Space K.K.™, The Mempool Open Source Project™, mempool.space™, the mempool logo®, the mempool.space logos™, the mempool square logo®, and the mempool blocks 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 Space K.K.™, The Mempool Open Source Project®, mempool.space™, the mempool logo®, the mempool.space logos™, the mempool square logo®, and the mempool blocks 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/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html
index 0bac1f9ff..c188e42dd 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
@@ -2,7 +2,7 @@
-
The Mempool Open Source Project ™
+
The Mempool Open Source Project ®
Explore the full Bitcoin ecosystem ™
diff --git a/frontend/src/index.bisq.html b/frontend/src/index.bisq.html
index 43c85b404..6d3bcb130 100644
--- a/frontend/src/index.bisq.html
+++ b/frontend/src/index.bisq.html
@@ -7,7 +7,7 @@
-
+
@@ -15,7 +15,7 @@
-
+
diff --git a/frontend/src/index.liquid.html b/frontend/src/index.liquid.html
index 630f6a2ab..ce57e5873 100644
--- a/frontend/src/index.liquid.html
+++ b/frontend/src/index.liquid.html
@@ -7,7 +7,7 @@
-
+
@@ -15,7 +15,7 @@
-
+
diff --git a/frontend/src/index.mempool.html b/frontend/src/index.mempool.html
index 60f1b4421..ec026b63b 100644
--- a/frontend/src/index.mempool.html
+++ b/frontend/src/index.mempool.html
@@ -7,7 +7,7 @@
-
+
@@ -15,7 +15,7 @@
-
+
diff --git a/unfurler/src/index.ts b/unfurler/src/index.ts
index 0b423ff92..4a54a451a 100644
--- a/unfurler/src/index.ts
+++ b/unfurler/src/index.ts
@@ -171,7 +171,7 @@ class Server {
const { lang, path } = parseLanguageUrl(rawPath);
const matchedRoute = matchRoute(this.network, path);
let ogImageUrl = config.SERVER.HOST + (matchedRoute.staticImg || matchedRoute.fallbackImg);
- let ogTitle = 'The Mempool Open Source Project™';
+ let ogTitle = 'The Mempool Open Source Project®';
if (matchedRoute.render) {
ogImageUrl = `${config.SERVER.HOST}/render/${lang || 'en'}/preview${path}`;
@@ -184,7 +184,7 @@ class Server {
${ogTitle}
-
+
From 2d4bc9dbd6d5082097f4dfdac3d03e46543df629 Mon Sep 17 00:00:00 2001
From: wiz
Date: Sun, 16 Jul 2023 17:50:36 +0900
Subject: [PATCH 03/53] ops: Move electrs scripts to mempool/electrs repo
---
production/electrs-start-liquid | 24 ------------------
production/electrs-start-liquidtestnet | 24 ------------------
production/electrs-start-mainnet | 22 ----------------
production/electrs-start-signet | 23 -----------------
production/electrs-start-testnet | 23 -----------------
production/install | 35 +++-----------------------
6 files changed, 3 insertions(+), 148 deletions(-)
delete mode 100755 production/electrs-start-liquid
delete mode 100755 production/electrs-start-liquidtestnet
delete mode 100755 production/electrs-start-mainnet
delete mode 100755 production/electrs-start-signet
delete mode 100755 production/electrs-start-testnet
diff --git a/production/electrs-start-liquid b/production/electrs-start-liquid
deleted file mode 100755
index a28135836..000000000
--- a/production/electrs-start-liquid
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env zsh
-cd "${HOME}/electrs"
-#source "${HOME}/.cargo/env"
-#export PATH="${HOME}/.cargo/bin:${PATH}"
-
-until false
-do
- cargo run \
- --release \
- --features liquid \
- --bin electrs \
- -- \
- -vvv \
- --asset-db-path "${HOME}/asset_registry_db" \
- --address-search \
- --cors '*' \
- --db-dir __ELECTRS_DATA_ROOT__ \
- --network liquid \
- --daemon-dir "${HOME}" \
- --http-socket-file '/elements/socket/esplora-liquid-mainnet' \
- --cookie '__ELEMENTS_RPC_USER__:__ELEMENTS_RPC_PASS__' \
- --precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
- sleep 1
-done
diff --git a/production/electrs-start-liquidtestnet b/production/electrs-start-liquidtestnet
deleted file mode 100755
index 828e96533..000000000
--- a/production/electrs-start-liquidtestnet
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/local/bin/zsh
-cd "${HOME}/electrs"
-#source "${HOME}/.cargo/env"
-#export PATH="${HOME}/.cargo/bin:${PATH}"
-
-until false
-do
- cargo run \
- --release \
- --features liquid \
- --bin electrs \
- -- \
- -vv \
- --asset-db-path "${HOME}/asset_registry_testnet_db" \
- --address-search \
- --cors '*' \
- --db-dir __ELECTRS_DATA_ROOT__ \
- --network liquidtestnet \
- --daemon-dir "${HOME}" \
- --http-socket-file '/elements/socket/esplora-liquid-testnet' \
- --cookie '__ELEMENTS_RPC_USER__:__ELEMENTS_RPC_PASS__' \
- --precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
- sleep 1
-done
diff --git a/production/electrs-start-mainnet b/production/electrs-start-mainnet
deleted file mode 100755
index c6a8c4d54..000000000
--- a/production/electrs-start-mainnet
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env zsh
-cd "${HOME}/electrs"
-#source "${HOME}/.cargo/env"
-#export PATH="${HOME}/.cargo/bin:${PATH}"
-
-until false
-do
- cargo run \
- --release \
- --bin electrs \
- -- \
- -vvvv \
- --address-search \
- --cors '*' \
- --db-dir __ELECTRS_DATA_ROOT__ \
- --daemon-dir "${HOME}" \
- --http-socket-file '/bitcoin/socket/esplora-bitcoin-mainnet' \
- --cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
- --precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
-
- sleep 3
-done
diff --git a/production/electrs-start-signet b/production/electrs-start-signet
deleted file mode 100755
index 40e1d1115..000000000
--- a/production/electrs-start-signet
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env zsh
-cd "${HOME}/electrs"
-#source "${HOME}/.cargo/env"
-#export PATH="${HOME}/.cargo/bin:${PATH}"
-
-until false
-do
- cargo run \
- --release \
- --bin electrs \
- -- \
- -vv \
- --network signet \
- --address-search \
- --cors '*' \
- --db-dir __ELECTRS_DATA_ROOT__ \
- --daemon-rpc-addr '127.0.0.1:38332' \
- --daemon-dir "${HOME}" \
- --http-socket-file '/bitcoin/socket/esplora-bitcoin-signet' \
- --cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
- --precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
- sleep 1
-done
diff --git a/production/electrs-start-testnet b/production/electrs-start-testnet
deleted file mode 100755
index ce05de2de..000000000
--- a/production/electrs-start-testnet
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env zsh
-cd "${HOME}/electrs"
-#source $HOME/.cargo/env
-#export PATH=$HOME/.cargo/bin:$PATH
-
-until false
-do
- cargo run \
- --release \
- --bin electrs \
- -- \
- -vvvv \
- --network testnet \
- --address-search \
- --cors '*' \
- --db-dir __ELECTRS_DATA_ROOT__ \
- --daemon-dir "${HOME}" \
- --http-socket-file '/bitcoin/socket/esplora-bitcoin-testnet' \
- --cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
- --precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
-
- sleep 3
-done
diff --git a/production/install b/production/install
index 1121f5b4f..05b868ba0 100755
--- a/production/install
+++ b/production/install
@@ -1600,15 +1600,8 @@ fi
########################################
# Electrs instance for Bitcoin Mainnet #
########################################
-
if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then
- echo "[*] Installing Bitcoin Mainnet electrs start script"
- osSudo "${ROOT_USER}" install -c -o "${BITCOIN_USER}" -g "${BITCOIN_GROUP}" -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/electrs-start-mainnet" "${BITCOIN_ELECTRS_HOME}"
-
- echo "[*] Configuring Bitcoin Mainnet RPC credentials in electrs start script"
- osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_USER__/${BITCOIN_RPC_USER}/" "${BITCOIN_ELECTRS_HOME}/electrs-start-mainnet"
- osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_PASS__/${BITCOIN_RPC_PASS}/" "${BITCOIN_ELECTRS_HOME}/electrs-start-mainnet"
- osSudo "${ROOT_USER}" sed -i.orig "s!__ELECTRS_DATA_ROOT__!${ELECTRS_DATA_ROOT}!" "${BITCOIN_ELECTRS_HOME}/electrs-start-mainnet"
+ echo "[*] FIXME: must only crontab enabled daemons"
fi
########################################
@@ -1616,13 +1609,7 @@ fi
########################################
if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then
- echo "[*] Installing Bitcoin Testnet electrs start script"
- osSudo "${ROOT_USER}" install -c -o "${BITCOIN_USER}" -g "${BITCOIN_GROUP}" -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/electrs-start-testnet" "${BITCOIN_ELECTRS_HOME}"
-
- echo "[*] Configuring Bitcoin Testnet RPC credentials in electrs start script"
- osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_USER__/${BITCOIN_RPC_USER}/" "${BITCOIN_ELECTRS_HOME}/electrs-start-testnet"
- osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_PASS__/${BITCOIN_RPC_PASS}/" "${BITCOIN_ELECTRS_HOME}/electrs-start-testnet"
- osSudo "${ROOT_USER}" sed -i.orig "s!__ELECTRS_DATA_ROOT__!${ELECTRS_DATA_ROOT}!" "${BITCOIN_ELECTRS_HOME}/electrs-start-testnet"
+ echo "[*] FIXME: must only crontab enabled daemons"
fi
#######################################
@@ -1630,13 +1617,7 @@ fi
#######################################
if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then
- echo "[*] Installing Bitcoin Signet electrs start script"
- osSudo "${ROOT_USER}" install -c -o "${BITCOIN_USER}" -g "${BITCOIN_GROUP}" -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/electrs-start-signet" "${BITCOIN_ELECTRS_HOME}"
-
- echo "[*] Configuring Bitcoin Signet RPC credentials in electrs start script"
- osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_USER__/${BITCOIN_RPC_USER}/" "${BITCOIN_ELECTRS_HOME}/electrs-start-signet"
- osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_PASS__/${BITCOIN_RPC_PASS}/" "${BITCOIN_ELECTRS_HOME}/electrs-start-signet"
- osSudo "${ROOT_USER}" sed -i.orig "s!__ELECTRS_DATA_ROOT__!${ELECTRS_DATA_ROOT}!" "${BITCOIN_ELECTRS_HOME}/electrs-start-signet"
+ echo "[*] FIXME: must only crontab enabled daemons"
fi
########################################
@@ -1644,21 +1625,12 @@ fi
########################################
if [ "${ELEMENTS_LIQUID_ENABLE}" = ON ];then
- echo "[*] Installing Elements Liquid electrs start script"
- osSudo "${ROOT_USER}" install -c -o "${ELEMENTS_USER}" -g "${ELEMENTS_GROUP}" -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/electrs-start-liquid" "${ELEMENTS_ELECTRS_HOME}"
-
echo "[*] Installing Elements crontab"
case $OS in
FreeBSD)
- echo "[*] FIXME: must only crontab enabled daemons"
osSudo "${ROOT_USER}" crontab -u "${ELEMENTS_USER}" "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/elements.crontab"
;;
esac
-
- echo "[*] Configuring Elements Liquid RPC credentials in electrs start script"
- osSudo "${ROOT_USER}" sed -i.orig "s/__ELEMENTS_RPC_USER__/${ELEMENTS_RPC_USER}/" "${ELEMENTS_ELECTRS_HOME}/electrs-start-liquid"
- osSudo "${ROOT_USER}" sed -i.orig "s/__ELEMENTS_RPC_PASS__/${ELEMENTS_RPC_PASS}/" "${ELEMENTS_ELECTRS_HOME}/electrs-start-liquid"
- osSudo "${ROOT_USER}" sed -i.orig "s!__ELECTRS_DATA_ROOT__!${ELECTRS_DATA_ROOT}!" "${ELEMENTS_ELECTRS_HOME}/electrs-start-liquid"
fi
################################################
@@ -1687,7 +1659,6 @@ fi
echo "[*] Installing crontabs"
case $OS in
FreeBSD)
- echo "[*] FIXME: must only crontab enabled daemons"
osSudo "${ROOT_USER}" crontab -u "${BITCOIN_USER}" "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/bitcoin.crontab"
osSudo "${ROOT_USER}" crontab -u "${MINFEE_USER}" "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/minfee.crontab"
;;
From 7970df27adaea7f1f8439a860de14c109c0a5dd7 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Sat, 22 Jul 2023 17:42:02 +0900
Subject: [PATCH 04/53] [graphs] fix min height in mobile landscape
---
.../block-fee-rates-graph/block-fee-rates-graph.component.scss | 3 ++-
.../block-fees-graph/block-fees-graph.component.scss | 3 ++-
.../block-health-graph/block-health-graph.component.scss | 3 ++-
.../block-rewards-graph/block-rewards-graph.component.scss | 3 ++-
.../block-sizes-weights-graph.component.scss | 3 ++-
.../components/hashrate-chart/hashrate-chart.component.scss | 3 ++-
.../hashrates-chart-pools/hashrate-chart-pools.component.scss | 3 ++-
frontend/src/app/lightning/nodes-map/nodes-map.component.scss | 3 ++-
.../nodes-networks-chart/nodes-networks-chart.component.scss | 3 ++-
.../statistics-chart/lightning-statistics-chart.component.scss | 3 ++-
10 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss
index 47c87a45c..f4f4dcc77 100644
--- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss
+++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss
index fae81952b..b73d55685 100644
--- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss
+++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.scss b/frontend/src/app/components/block-health-graph/block-health-graph.component.scss
index f8403bad5..7b8154bae 100644
--- a/frontend/src/app/components/block-health-graph/block-health-graph.component.scss
+++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss
index f8403bad5..7b8154bae 100644
--- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss
+++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss
index f8403bad5..7b8154bae 100644
--- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss
+++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss
index 0caa35f33..886608573 100644
--- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss
+++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss
index 3b1083505..64a4dcb3d 100644
--- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss
+++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.scss b/frontend/src/app/lightning/nodes-map/nodes-map.component.scss
index d49b68957..a2f62e9c5 100644
--- a/frontend/src/app/lightning/nodes-map/nodes-map.component.scss
+++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.scss
@@ -14,7 +14,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss
index bb8f2cd87..0e6fb056d 100644
--- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss
+++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss
index 0d692a6c8..c885e4839 100644
--- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss
+++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss
@@ -25,7 +25,8 @@
flex-direction: column;
padding: 0px 15px;
width: 100%;
- height: calc(100vh - 250px);
+ height: calc(100vh - 225px);
+ min-height: 400px;
@media (min-width: 992px) {
height: calc(100vh - 150px);
}
From 05affa5ad4320f0b2bc4475f4c483a497ab22f15 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Sun, 23 Jul 2023 16:19:48 +0900
Subject: [PATCH 05/53] Fix difficulty tooltip position
---
.../app/components/difficulty/difficulty.component.ts | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts
index d3983c939..a2c03dc56 100644
--- a/frontend/src/app/components/difficulty/difficulty.component.ts
+++ b/frontend/src/app/components/difficulty/difficulty.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, Component, HostListener, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { combineLatest, Observable, timer } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { StateService } from '../..//services/state.service';
@@ -61,6 +61,7 @@ export class DifficultyComponent implements OnInit {
constructor(
public stateService: StateService,
+ private cd: ChangeDetectorRef,
@Inject(LOCALE_ID) private locale: string,
) { }
@@ -189,9 +190,15 @@ export class DifficultyComponent implements OnInit {
return shapes;
}
+ @HostListener('pointerdown', ['$event'])
+ onPointerDown(event) {
+ this.onPointerMove(event);
+ }
+
@HostListener('pointermove', ['$event'])
onPointerMove(event) {
this.tooltipPosition = { x: event.clientX, y: event.clientY };
+ this.cd.markForCheck();
}
onHover(event, rect): void {
From 1fd5b975f152c085e21a64f86ccc205a560e2884 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Thu, 27 Jul 2023 11:45:16 +0900
Subject: [PATCH 06/53] Handle failures while fetching block transactions
---
backend/src/api/blocks.ts | 64 +++++++++++++++++++---------
backend/src/api/transaction-utils.ts | 20 ++++++++-
2 files changed, 62 insertions(+), 22 deletions(-)
diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts
index 4dbf4305e..9ad9278d0 100644
--- a/backend/src/api/blocks.ts
+++ b/backend/src/api/blocks.ts
@@ -105,11 +105,16 @@ class Blocks {
}
}
- // Skip expensive lookups while mempool has priority
if (onlyCoinbase) {
try {
- const coinbase = await transactionUtils.$getTransactionExtended(txIds[0], false, false, false, addMempoolData);
- return [coinbase];
+ const coinbase = await transactionUtils.$getTransactionExtendedRetry(txIds[0], false, false, false, addMempoolData);
+ if (coinbase && coinbase.vin[0].is_coinbase) {
+ return [coinbase];
+ } else {
+ const msg = `Expected a coinbase tx, but the backend API returned something else`;
+ logger.err(msg);
+ throw new Error(msg);
+ }
} catch (e) {
const msg = `Cannot fetch coinbase tx ${txIds[0]}. Reason: ` + (e instanceof Error ? e.message : e);
logger.err(msg);
@@ -134,17 +139,17 @@ class Blocks {
// Fetch remaining txs individually
for (const txid of txIds.filter(txid => !transactionMap[txid])) {
- if (!transactionMap[txid]) {
- if (!quiet && (totalFound % (Math.round((txIds.length) / 10)) === 0 || totalFound + 1 === txIds.length)) { // Avoid log spam
- logger.debug(`Indexing tx ${totalFound + 1} of ${txIds.length} in block #${blockHeight}`);
- }
- try {
- const tx = await transactionUtils.$getTransactionExtended(txid, false, false, false, addMempoolData);
- transactionMap[txid] = tx;
- totalFound++;
- } catch (e) {
- logger.err(`Cannot fetch tx ${txid}. Reason: ` + (e instanceof Error ? e.message : e));
- }
+ if (!quiet && (totalFound % (Math.round((txIds.length) / 10)) === 0 || totalFound + 1 === txIds.length)) { // Avoid log spam
+ logger.debug(`Indexing tx ${totalFound + 1} of ${txIds.length} in block #${blockHeight}`);
+ }
+ try {
+ const tx = await transactionUtils.$getTransactionExtendedRetry(txid, false, false, false, addMempoolData);
+ transactionMap[txid] = tx;
+ totalFound++;
+ } catch (e) {
+ const msg = `Cannot fetch tx ${txid}. Reason: ` + (e instanceof Error ? e.message : e);
+ logger.err(msg);
+ throw new Error(msg);
}
}
@@ -152,8 +157,25 @@ class Blocks {
logger.debug(`${foundInMempool} of ${txIds.length} found in mempool. ${totalFound - foundInMempool} fetched through backend service.`);
}
+ // Require the first transaction to be a coinbase
+ const coinbase = transactionMap[txIds[0]];
+ if (!coinbase || !coinbase.vin[0].is_coinbase) {
+ console.log(coinbase);
+ const msg = `Expected first tx in a block to be a coinbase, but found something else`;
+ logger.err(msg);
+ throw new Error(msg);
+ }
+
+ // Require all transactions to be present
+ // (we should have thrown an error already if a tx request failed)
+ if (txIds.some(txid => !transactionMap[txid])) {
+ const msg = `Failed to fetch ${txIds.length - totalFound} transactions from block`;
+ logger.err(msg);
+ throw new Error(msg);
+ }
+
// Return list of transactions, preserving block order
- return txIds.map(txid => transactionMap[txid]).filter(tx => tx != null);
+ return txIds.map(txid => transactionMap[txid]);
}
/**
@@ -667,14 +689,14 @@ class Blocks {
const block = BitcoinApi.convertBlock(verboseBlock);
const txIds: string[] = verboseBlock.tx.map(tx => tx.txid);
const transactions = await this.$getTransactionsExtended(blockHash, block.height, false, txIds, false, true) as MempoolTransactionExtended[];
- if (config.MEMPOOL.BACKEND !== 'esplora') {
- // fill in missing transaction fee data from verboseBlock
- for (let i = 0; i < transactions.length; i++) {
- if (!transactions[i].fee && transactions[i].txid === verboseBlock.tx[i].txid) {
- transactions[i].fee = verboseBlock.tx[i].fee * 100_000_000;
- }
+
+ // fill in missing transaction fee data from verboseBlock
+ for (let i = 0; i < transactions.length; i++) {
+ if (!transactions[i].fee && transactions[i].txid === verboseBlock.tx[i].txid) {
+ transactions[i].fee = verboseBlock.tx[i].fee * 100_000_000;
}
}
+
const cpfpSummary: CpfpSummary = Common.calculateCpfp(block.height, transactions);
const blockExtended: BlockExtended = await this.$getBlockExtended(block, cpfpSummary.transactions);
const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions);
diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts
index 0b10afdfb..009fe1dde 100644
--- a/backend/src/api/transaction-utils.ts
+++ b/backend/src/api/transaction-utils.ts
@@ -3,6 +3,7 @@ import { IEsploraApi } from './bitcoin/esplora-api.interface';
import { Common } from './common';
import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory';
import * as bitcoinjs from 'bitcoinjs-lib';
+import logger from '../logger';
class TransactionUtils {
constructor() { }
@@ -22,6 +23,23 @@ class TransactionUtils {
};
}
+ // Wrapper for $getTransactionExtended with an automatic retry direct to Core if the first API request fails.
+ // Propagates any error from the retry request.
+ public async $getTransactionExtendedRetry(txid: string, addPrevouts = false, lazyPrevouts = false, forceCore = false, addMempoolData = false): Promise {
+ try {
+ const result = await this.$getTransactionExtended(txid, addPrevouts, lazyPrevouts, forceCore, addMempoolData);
+ if (result) {
+ return result;
+ } else {
+ logger.err(`Cannot fetch tx ${txid}. Reason: backend returned null data`);
+ }
+ } catch (e) {
+ logger.err(`Cannot fetch tx ${txid}. Reason: ` + (e instanceof Error ? e.message : e));
+ }
+ // retry direct from Core if first request failed
+ return this.$getTransactionExtended(txid, addPrevouts, lazyPrevouts, true, addMempoolData);
+ }
+
/**
* @param txId
* @param addPrevouts
@@ -31,7 +49,7 @@ class TransactionUtils {
public async $getTransactionExtended(txId: string, addPrevouts = false, lazyPrevouts = false, forceCore = false, addMempoolData = false): Promise {
let transaction: IEsploraApi.Transaction;
if (forceCore === true) {
- transaction = await bitcoinCoreApi.$getRawTransaction(txId, true);
+ transaction = await bitcoinCoreApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
} else {
transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
}
From 589adb95c304bad01a9d32c12264325f341a2954 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Thu, 27 Jul 2023 14:49:21 +0900
Subject: [PATCH 07/53] remove stray debugging log
---
backend/src/api/blocks.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts
index 9ad9278d0..ba7a55149 100644
--- a/backend/src/api/blocks.ts
+++ b/backend/src/api/blocks.ts
@@ -160,7 +160,6 @@ class Blocks {
// Require the first transaction to be a coinbase
const coinbase = transactionMap[txIds[0]];
if (!coinbase || !coinbase.vin[0].is_coinbase) {
- console.log(coinbase);
const msg = `Expected first tx in a block to be a coinbase, but found something else`;
logger.err(msg);
throw new Error(msg);
From 3f3f0db2f2631a977d4817c0cadf256f15543a62 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Fri, 28 Jul 2023 13:45:04 +0900
Subject: [PATCH 08/53] [mining] use .slug to load pool logo
---
.../src/app/components/blocks-list/blocks-list.component.ts | 4 ++--
frontend/src/app/components/pool/pool-preview.component.ts | 2 +-
frontend/src/app/components/pool/pool.component.ts | 2 +-
frontend/src/app/dashboard/dashboard.component.ts | 2 +-
frontend/src/app/interfaces/node-api.interface.ts | 1 +
frontend/src/app/services/mining.service.ts | 2 +-
6 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts
index 1af6572fc..cec925270 100644
--- a/frontend/src/app/components/blocks-list/blocks-list.component.ts
+++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts
@@ -68,7 +68,7 @@ export class BlocksList implements OnInit {
for (const block of blocks) {
// @ts-ignore: Need to add an extra field for the template
block.extras.pool.logo = `/resources/mining-pools/` +
- block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
+ block.extras.pool.slug + '.svg';
}
}
if (this.widget) {
@@ -102,7 +102,7 @@ export class BlocksList implements OnInit {
if (this.stateService.env.MINING_DASHBOARD) {
// @ts-ignore: Need to add an extra field for the template
blocks[1][0].extras.pool.logo = `/resources/mining-pools/` +
- blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
+ blocks[1][0].extras.pool.slug + '.svg';
}
acc.unshift(blocks[1][0]);
acc = acc.slice(0, this.widget ? 6 : 15);
diff --git a/frontend/src/app/components/pool/pool-preview.component.ts b/frontend/src/app/components/pool/pool-preview.component.ts
index 0431686d6..e03b73665 100644
--- a/frontend/src/app/components/pool/pool-preview.component.ts
+++ b/frontend/src/app/components/pool/pool-preview.component.ts
@@ -89,7 +89,7 @@ export class PoolPreviewComponent implements OnInit {
this.openGraphService.waitOver('pool-stats-' + this.slug);
- const logoSrc = `/resources/mining-pools/` + poolStats.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
+ const logoSrc = `/resources/mining-pools/` + poolStats.pool.slug + '.svg';
if (logoSrc === this.lastImgSrc) {
this.openGraphService.waitOver('pool-img-' + this.slug);
}
diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts
index f2fc79ff2..edd5801fe 100644
--- a/frontend/src/app/components/pool/pool.component.ts
+++ b/frontend/src/app/components/pool/pool.component.ts
@@ -79,7 +79,7 @@ export class PoolComponent implements OnInit {
poolStats.pool.regexes = regexes.slice(0, -3);
return Object.assign({
- logo: `/resources/mining-pools/` + poolStats.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'
+ logo: `/resources/mining-pools/` + poolStats.pool.slug + '.svg'
}, poolStats);
})
);
diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts
index 05381453d..645ccc8d8 100644
--- a/frontend/src/app/dashboard/dashboard.component.ts
+++ b/frontend/src/app/dashboard/dashboard.component.ts
@@ -159,7 +159,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
for (const block of blocks) {
// @ts-ignore: Need to add an extra field for the template
block.extras.pool.logo = `/resources/mining-pools/` +
- block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
+ block.extras.pool.slug + '.svg';
}
}
return of(blocks.slice(0, 6));
diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts
index 4249fd9db..59dff8e90 100644
--- a/frontend/src/app/interfaces/node-api.interface.ts
+++ b/frontend/src/app/interfaces/node-api.interface.ts
@@ -110,6 +110,7 @@ export interface PoolInfo {
regexes: string; // JSON array
addresses: string; // JSON array
emptyBlocks: number;
+ slug: string;
}
export interface PoolStat {
pool: PoolInfo;
diff --git a/frontend/src/app/services/mining.service.ts b/frontend/src/app/services/mining.service.ts
index 63257fa74..96723921e 100644
--- a/frontend/src/app/services/mining.service.ts
+++ b/frontend/src/app/services/mining.service.ts
@@ -96,7 +96,7 @@ export class MiningService {
share: parseFloat((poolStat.blockCount / stats.blockCount * 100).toFixed(2)),
lastEstimatedHashrate: (poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / hashrateDivider).toFixed(2),
emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
- logo: `/resources/mining-pools/` + poolStat.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg',
+ logo: `/resources/mining-pools/` + poolStat.slug + '.svg',
...poolStat
};
});
From 2c613195cce70672f2312ce02f8a4bc1771227a0 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Fri, 28 Jul 2023 16:35:42 +0900
Subject: [PATCH 09/53] Add support for compressed p2pk addresses
---
backend/src/api/websocket-handler.ts | 7 +++++--
.../components/address/address-preview.component.ts | 4 ++--
.../src/app/components/address/address.component.ts | 6 +++---
.../components/search-form/search-form.component.ts | 2 +-
.../transactions-list.component.html | 12 ++++++------
frontend/src/app/services/electrs-api.service.ts | 3 ++-
6 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts
index 74c4ed832..0d0332523 100644
--- a/backend/src/api/websocket-handler.ts
+++ b/backend/src/api/websocket-handler.ts
@@ -183,15 +183,18 @@ class WebsocketHandler {
}
if (parsedMessage && parsedMessage['track-address']) {
- if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|[0-9a-fA-F]{130})$/
+ if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64})$/
.test(parsedMessage['track-address'])) {
let matchedAddress = parsedMessage['track-address'];
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(parsedMessage['track-address'])) {
matchedAddress = matchedAddress.toLowerCase();
}
- if (/^[0-9a-fA-F]{130}$/.test(parsedMessage['track-address'])) {
+ if (/^04[a-fA-F0-9]{128}$/.test(parsedMessage['track-address'])) {
client['track-address'] = null;
client['track-scriptpubkey'] = '41' + matchedAddress + 'ac';
+ } else if (/^|(02|03)[a-fA-F0-9]{64}$/.test(parsedMessage['track-address'])) {
+ client['track-address'] = null;
+ client['track-scriptpubkey'] = '21' + matchedAddress + 'ac';
} else {
client['track-address'] = matchedAddress;
client['track-scriptpubkey'] = null;
diff --git a/frontend/src/app/components/address/address-preview.component.ts b/frontend/src/app/components/address/address-preview.component.ts
index 07ead8baa..844def9fd 100644
--- a/frontend/src/app/components/address/address-preview.component.ts
+++ b/frontend/src/app/components/address/address-preview.component.ts
@@ -64,12 +64,12 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
this.address = null;
this.addressInfo = null;
this.addressString = params.get('id') || '';
- if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|[A-F0-9]{130}$/.test(this.addressString)) {
+ if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(this.addressString)) {
this.addressString = this.addressString.toLowerCase();
}
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
- return (this.addressString.match(/[a-f0-9]{130}/)
+ return (this.addressString.match(/04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}/)
? this.electrsApiService.getPubKeyAddress$(this.addressString)
: this.electrsApiService.getAddress$(this.addressString)
).pipe(
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts
index ae1f6dbbe..a6cbb9617 100644
--- a/frontend/src/app/components/address/address.component.ts
+++ b/frontend/src/app/components/address/address.component.ts
@@ -72,7 +72,7 @@ export class AddressComponent implements OnInit, OnDestroy {
this.addressInfo = null;
document.body.scrollTo(0, 0);
this.addressString = params.get('id') || '';
- if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|[A-F0-9]{130}$/.test(this.addressString)) {
+ if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(this.addressString)) {
this.addressString = this.addressString.toLowerCase();
}
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
@@ -84,7 +84,7 @@ export class AddressComponent implements OnInit, OnDestroy {
)
.pipe(
switchMap(() => (
- this.addressString.match(/[a-f0-9]{130}/)
+ this.addressString.match(/04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}/)
? this.electrsApiService.getPubKeyAddress$(this.addressString)
: this.electrsApiService.getAddress$(this.addressString)
).pipe(
@@ -118,7 +118,7 @@ export class AddressComponent implements OnInit, OnDestroy {
this.isLoadingAddress = false;
this.isLoadingTransactions = true;
return address.is_pubkey
- ? this.electrsApiService.getScriptHashTransactions$('41' + address.address + 'ac')
+ ? this.electrsApiService.getScriptHashTransactions$((address.address.length === 66 ? '21' : '41') + address.address + 'ac')
: this.electrsApiService.getAddressTransactions$(address.address);
}),
switchMap((transactions) => {
diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts
index 61b3351b7..0a794d1f5 100644
--- a/frontend/src/app/components/search-form/search-form.component.ts
+++ b/frontend/src/app/components/search-form/search-form.component.ts
@@ -34,7 +34,7 @@ export class SearchFormComponent implements OnInit {
}
}
- regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[A-z]{2,5}1[a-zA-HJ-NP-Z0-9]{39,59}|[0-9a-fA-F]{130})$/;
+ regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[A-z]{2,5}1[a-zA-HJ-NP-Z0-9]{39,59}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64})$/;
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
regexTransaction = /^([a-fA-F0-9]{64})(:\d+)?$/;
regexBlockheight = /^[0-9]{1,9}$/;
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 d1d0673fe..22486d320 100644
--- a/frontend/src/app/components/transactions-list/transactions-list.component.html
+++ b/frontend/src/app/components/transactions-list/transactions-list.component.html
@@ -23,7 +23,7 @@
@@ -56,8 +56,8 @@
Peg-in
- P2PK
-
+ P2PK
+
@@ -184,7 +184,7 @@
@@ -192,8 +192,8 @@
- P2PK
-
+ P2PK
+
diff --git a/frontend/src/app/services/electrs-api.service.ts b/frontend/src/app/services/electrs-api.service.ts
index f866eb23d..d63d49f68 100644
--- a/frontend/src/app/services/electrs-api.service.ts
+++ b/frontend/src/app/services/electrs-api.service.ts
@@ -67,7 +67,8 @@ export class ElectrsApiService {
}
getPubKeyAddress$(pubkey: string): Observable {
- return this.getScriptHash$('41' + pubkey + 'ac').pipe(
+ const scriptpubkey = (pubkey.length === 130 ? '41' : '21') + pubkey + 'ac';
+ return this.getScriptHash$(scriptpubkey).pipe(
switchMap((scripthash: ScriptHash) => {
return of({
...scripthash,
From cc27c0159e393270bc5e1dda8cab0d3c107c9203 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 28 Jul 2023 23:04:44 +0200
Subject: [PATCH 10/53] [BUG]: Update frontend entrypoint.sh
Typo of variable LIQUID_ENABLED
---
docker/frontend/entrypoint.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/frontend/entrypoint.sh b/docker/frontend/entrypoint.sh
index b6946578b..7d5ee313d 100644
--- a/docker/frontend/entrypoint.sh
+++ b/docker/frontend/entrypoint.sh
@@ -18,7 +18,7 @@ fi
__TESTNET_ENABLED__=${TESTNET_ENABLED:=false}
__SIGNET_ENABLED__=${SIGNET_ENABLED:=false}
-__LIQUID_ENABLED__=${LIQUID_EANBLED:=false}
+__LIQUID_ENABLED__=${LIQUID_ENABLED:=false}
__LIQUID_TESTNET_ENABLED__=${LIQUID_TESTNET_ENABLED:=false}
__BISQ_ENABLED__=${BISQ_ENABLED:=false}
__BISQ_SEPARATE_BACKEND__=${BISQ_SEPARATE_BACKEND:=false}
From 354c119e9990fe30788c6cc09e607d8847702533 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Sat, 29 Jul 2023 15:16:28 +0900
Subject: [PATCH 11/53] fix websocket connection state observable
---
.../src/app/dashboard/dashboard.component.ts | 20 +++++++++++++------
.../src/app/services/websocket.service.ts | 2 +-
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts
index 05381453d..38ee5f436 100644
--- a/frontend/src/app/dashboard/dashboard.component.ts
+++ b/frontend/src/app/dashboard/dashboard.component.ts
@@ -1,6 +1,6 @@
import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { combineLatest, merge, Observable, of, Subscription } from 'rxjs';
-import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators';
+import { catchError, filter, map, scan, share, switchMap, tap } from 'rxjs/operators';
import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface';
import { MempoolInfo, TransactionStripped, ReplacementInfo } from '../interfaces/websocket.interface';
import { ApiService } from '../services/api.service';
@@ -171,7 +171,11 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
this.mempoolStats$ = this.stateService.connectionState$
.pipe(
filter((state) => state === 2),
- switchMap(() => this.apiService.list2HStatistics$()),
+ switchMap(() => this.apiService.list2HStatistics$().pipe(
+ catchError((e) => {
+ return of(null);
+ })
+ )),
switchMap((mempoolStats) => {
return merge(
this.stateService.live2Chart$
@@ -186,10 +190,14 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
);
}),
map((mempoolStats) => {
- return {
- mempool: mempoolStats,
- weightPerSecond: this.handleNewMempoolData(mempoolStats.concat([])),
- };
+ if (mempoolStats) {
+ return {
+ mempool: mempoolStats,
+ weightPerSecond: this.handleNewMempoolData(mempoolStats.concat([])),
+ };
+ } else {
+ return null;
+ }
}),
share(),
);
diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts
index e70424cdc..4bd20e987 100644
--- a/frontend/src/app/services/websocket.service.ts
+++ b/frontend/src/app/services/websocket.service.ts
@@ -113,7 +113,7 @@ export class WebsocketService {
this.stateService.connectionState$.next(2);
}
- if (this.stateService.connectionState$.value === 1) {
+ if (this.stateService.connectionState$.value !== 2) {
this.stateService.connectionState$.next(2);
}
From 2719be90751fb2309abc11dc7c9d7f6df14f422c Mon Sep 17 00:00:00 2001
From: Czino
Date: Sat, 29 Jul 2023 09:53:47 +0200
Subject: [PATCH 12/53] Add contributor license agreement
---
contributors/Czino.txt | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 contributors/Czino.txt
diff --git a/contributors/Czino.txt b/contributors/Czino.txt
new file mode 100644
index 000000000..275149d7c
--- /dev/null
+++ b/contributors/Czino.txt
@@ -0,0 +1,3 @@
+I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022.
+
+Signed: Czino
From 1f8f40011a83b337011b88c44b1b7735a22cae7b Mon Sep 17 00:00:00 2001
From: Czino
Date: Sat, 29 Jul 2023 09:57:40 +0200
Subject: [PATCH 13/53] Update date
---
contributors/Czino.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contributors/Czino.txt b/contributors/Czino.txt
index 275149d7c..08affb095 100644
--- a/contributors/Czino.txt
+++ b/contributors/Czino.txt
@@ -1,3 +1,3 @@
-I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022.
+I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of July 29, 2023.
Signed: Czino
From 562a5f68788dbf5c1de2dab3fe556f501bac7b1c Mon Sep 17 00:00:00 2001
From: Rishabh
Date: Sat, 29 Jul 2023 15:54:34 +0530
Subject: [PATCH 14/53] rishkwal contributor license agreement added
---
contributors/rishkwal.txt | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 contributors/rishkwal.txt
diff --git a/contributors/rishkwal.txt b/contributors/rishkwal.txt
new file mode 100644
index 000000000..9a50bda6b
--- /dev/null
+++ b/contributors/rishkwal.txt
@@ -0,0 +1,3 @@
+I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of July 29, 2023.
+
+Signed: rishkwal
From 2670589293a12027d9ca5594da1525c4c4ddf735 Mon Sep 17 00:00:00 2001
From: fiatjaf_
Date: Sat, 29 Jul 2023 09:27:12 -0300
Subject: [PATCH 15/53] Create fiatjaf.txt
---
contributors/fiatjaf.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 contributors/fiatjaf.txt
diff --git a/contributors/fiatjaf.txt b/contributors/fiatjaf.txt
new file mode 100644
index 000000000..cdd716d3c
--- /dev/null
+++ b/contributors/fiatjaf.txt
@@ -0,0 +1,5 @@
+I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022.
+I also regret having ever contributed to this repository since they keep asking me to sign this legalese timewaste things.
+And finally I don't care about licenses and won't sue anyone over intellectual property, which is a fake statist construct invented by evil lobby lawyers.
+
+Signed: fiatjaf
From 945a8ce92e7bc99d3ae3b1dcde746cf1799536c1 Mon Sep 17 00:00:00 2001
From: Mononaut
Date: Sun, 30 Jul 2023 18:56:57 +0900
Subject: [PATCH 16/53] Use log10 scale for projected block fee graph
---
.../fee-distribution-graph.component.ts | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts
index f275588a1..212510e71 100644
--- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts
+++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts
@@ -74,14 +74,14 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr
this.labelInterval = this.numSamples / this.numLabels;
while (nextSample <= maxBlockVSize) {
if (txIndex >= txs.length) {
- samples.push([(1 - (sampleIndex / this.numSamples)) * 100, 0]);
+ samples.push([(1 - (sampleIndex / this.numSamples)) * 100, 0.000001]);
nextSample += sampleInterval;
sampleIndex++;
continue;
}
while (txs[txIndex] && nextSample < cumVSize + txs[txIndex].vsize) {
- samples.push([(1 - (sampleIndex / this.numSamples)) * 100, txs[txIndex].rate]);
+ samples.push([(1 - (sampleIndex / this.numSamples)) * 100, txs[txIndex].rate || 0.000001]);
nextSample += sampleInterval;
sampleIndex++;
}
@@ -118,7 +118,9 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr
},
},
yAxis: {
- type: 'value',
+ type: 'log',
+ min: 1,
+ max: this.data.reduce((min, val) => Math.max(min, val[1]), 1),
// name: 'Effective Fee Rate s/vb',
// nameLocation: 'middle',
splitLine: {
@@ -129,12 +131,16 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr
}
},
axisLabel: {
+ show: true,
formatter: (value: number): string => {
const unitValue = this.weightMode ? value / 4 : value;
const selectedPowerOfTen = selectPowerOfTen(unitValue);
const newVal = Math.round(unitValue / selectedPowerOfTen.divider);
return `${newVal}${selectedPowerOfTen.unit}`;
},
+ },
+ axisTick: {
+ show: true,
}
},
series: [{
From c88b7ddc7732201b17b9e54c50b1df32f147644e Mon Sep 17 00:00:00 2001
From: wiz
Date: Mon, 31 Jul 2023 11:12:54 +0900
Subject: [PATCH 17/53] ops: Use TK7 for unfurler
---
production/unfurler-config.mainnet.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/production/unfurler-config.mainnet.json b/production/unfurler-config.mainnet.json
index 77df23704..fd389476b 100644
--- a/production/unfurler-config.mainnet.json
+++ b/production/unfurler-config.mainnet.json
@@ -1,6 +1,6 @@
{
"SERVER": {
- "HOST": "https://mempool.fra.mempool.space",
+ "HOST": "https://mempool.tk7.mempool.space",
"HTTP_PORT": 8001
},
"MEMPOOL": {
From 16401044f6e1da00c932575876f4a78ce8aed624 Mon Sep 17 00:00:00 2001
From: wiz
Date: Mon, 31 Jul 2023 11:13:56 +0900
Subject: [PATCH 18/53] Remove text from mempool.space preview image
---
.../src/resources/mempool-space-preview.png | Bin 315182 -> 295855 bytes
frontend/src/resources/previews/dashboard.png | Bin 742892 -> 295855 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/frontend/src/resources/mempool-space-preview.png b/frontend/src/resources/mempool-space-preview.png
index bfc59f60017e436a49a081e0a3e022e5300b15a9..60c8bcc6ac4a1ef484e3df91b4fada5e068426c4 100644
GIT binary patch
literal 295855
zcmeFac|6p6`#)|AC8?v6B2{btq#D2^A43`%+}dz78{HCds}V
z48|B+48x2V24ncX-P>{Q``qVq|NDFV&L5bEhnacJ>wUeh>v=t&&+F~gU63a4KEZt~
zEG)d*S~u^puyA#=u&|T3If37VncIE=e(d$oGWKR+IeB&Gi}lur%a$xGCs?#^Ucdh|
zWuCwlclg0@<4*$)gNe7^F&r(|)%WbTRDT}C{XC(Z`_s`n@EQ9{mIsa+9ljJnL`=VU
z9kI7hZl?Cm@lOb<-zXjm`QrQqtIPL5W|`_unN3Th?4vRtd|d8By?z#(@+o$tCtcP{Jy@&>FDi-HF;!ZY|C
z`u=<+|5$1e|5IM>ZjJ7}vQGc^>VfBc-CN7aK{`p|_#Z#<7>7AK8||PE+Z)0EV{87t
zAP=@;R#ty*rQrW@gRTbSf>=~IQq?m#|7_p?@o8B&Q@%Q@2Z=lfWf-vkU)Sny&jF47
zwf%n*cYkgFpPsV6qW@n9?~l^mU&-fBjuwzE|4Kf8Qc79(|CN0HNUXEzYEy^ReAra
zyt}x&znb1%XX8H~{?+vUf4=Fxi{QOq+;e)-HKQl|=-AbrYkc?0R`N;0w%Zs+`Z-m4
zH@6PTEBFsWTzyt{j2`aF2={#4l6zeVdgmYH_uGzb-xKOh{4Fgp
zH<{`8iU)aBN>S?aZ*A0ZHlKqYr*_3CcR4Fvv}`3BFNhu}!rv2=rJU$k9kO^_siHOP
zLptxOEDYlDtnwmR=$dnm7dNM7{r}L&P;6BE8FLkJS`4
zImsslee^*Ja(aL^(+~nI@%iPfmGAj0viVFh_V^xYwd?Kdi&Gnr69YxmUvAeYybvMcU|GZ+3f?$zj
zo@o8#QSq1kEDzI_eNW-Hzx6PhW*5h^WZs9Co
z4YyObsjzLClL*ymY15m4a!fYt{kP$aBKjEba-J)tTW`m
z3&&eLy(od}q+7ozVb$L}?%?jx@VM5yvu}3=_aDsQB695(pi-__umiCbw;u9a%vxrU
zVDrAAGpd;%_7>?-U;3cnOjTFD*P$X5`+pu7HlO_-BD>K1L@M1BJ;9sgb4lQS^_zdncLy`9gO+#I`yj(lPX8fD
z-Mzx=pf&tup{}%N=EaQd`RZuuGHY+uq}9E0U+-_r2ahNJ{gm8i2cjL#w_EDFYKsQ+
zxf5rXxpoIaHf(UfUf6M@L|csS!~6r+&z)#Cer(7twX~%$NTZB+s-3frz8kjsmkI(1UbU
z>09H^tl}3^8&S9N*^fRGc|IApBQFNWIs_de)zMl1`Pw_MJf|HH6ool#w@&PeqqFUz
zaFYs(WGdno71l{wR{!P=b;HkcJL1-x3uMh6GNhRMgg@b_6|>z6(=V2JW(5A0fPq}h
zGP7F&$+_A8A8KXB)X|cUxnWttbTM^4r>PoF0+58_#5U4ThlHgWO!?9-+uy-l&(VUj
zb2WvMgb_!?2dNU3a~0gspd!3c=0igu6Xrczm0u2wau3G@-^bteut;JEo{fTPqbL)_)=EuxzkqXA
z4_efhJOjbiPR%^`5P27fPoEFSo~fM};kuo{McpmU1{qERdg_7aS6~NsT@#y)a91Wf
z#7grVwv0c`Z}zel=&J2=-D;aK)2Bauu;Pb5&eEk3K4B@Ioi}HDoE-{aJjz$lZXwXm
z{%(_Z(=1%BW)|n)cRmX-Dv|)csMxtgqQt(}E?@6ogqN)cKW|^*=mUpHQ@^6zL8Lp1xM_qP-%b9?EaHk$H
zzdY?O6SgpCQ-B%$-(>ns6qmMwQuqI$PulBRLa5Dys#d&8mmpVK$8Fk^FmR
z-jpTf^o3tl#9x<-a(DX`+Ws@k{VledSMpd{l`dUX+`aE>;R{KX$;k$d9HctagBjRD
za_-LvsZ>epCW|xH(g_zP%esAx1+&vVmiB}qE`4LB0yVMWdDA5mw}IMBpSfNgB_E}`
zNHqJe~*|i&JZbCk`FqO_ek4TY-=IsVQd6jQ7I6dH~Nl
ztvEf%-@d`MU+^TCaIeL11=+3Y0j`kMeQK>AIw%*MO7P{y(<;Lv1REU>;;OYxW@8zTrk1uQO*Q^}VixguC`&Qd*n
z0qvkxF$}zS3GR>=Z7inoD~Z1|E+!N7E;BqbRu#&njrUdgg{FaxV_L$fl{w%ZdLPwF
zbN`2vO_3iaZ>OD;NcW05uDp5Luqh1{9cR~}+-&}SP8(F?RC;ijqG7;GqSGA-a^si#
zYINKkyK}*&FWoMZoq3&Bj`mvC*yeZILAZpWDf`tZHj3j}
zFu&+~e8|?;+Tse>u&rNIFu@s?`)*bEz}rzS(K8|8BJS7n!#~#F*1>GmXBic!%?a{s
z%=@CwuhxfQDc-N$H0P^*hVQ^d!tW3{X3C|^_^!-cTTnoq{c3I#*Ri~h)&Hk*_O5~*
z6g+YmFf?%OmDu+iL=4`)U1F9G(qW?oee)K&m+sYg=1SnV`}_gvu<^J-Z>uD~V>7AS
zezS<}o=^mze_+t4d><Ng@b!-;_c&K59c;U*n}X(1LTS=}}t
zmW-6!>?f4i<~sFfMjlxExOw5)1fCZ+i1|%2DzAx
z4CMQF0Y4jzmRj?R+dAMQj%+GsX<;_Q3bUMRxAxZgK;$g(sDPrq`;flMkFr6@=g+5Y
z#1|MZ2XAf=i2-mGY4?bL^?qEL*Nfp`ylOQQ
zj_V6>;^h7Se!0cvE=+4saD7Man$pVS1nPUD%rAnzZql*
zZ4cL%+KMMPuDkPQj9Y&RY{syBOdO)Ven0mbTYySYwtGH8kN&(goxJ^lie_A|a+h`h
zU>763zyxltxk1cx20#_%J}34n@Z_zH`V<(bC@82<&&9idHt(?Tauh;YZqqI
zsncw<>7hzGb1_qKX10d9`7=I|=j?o08QA=!y0mKjZv1!YZL%Ez-0HsVW&e#uJW;Ra
zLS_~o3l=dA8qLGAC_1-iJqj|9TO0vO?+=m|jiM56bL|a8o2MImzbMSJMMUsEIj(m@
z7&>qW3m@^%aT%#@p)U(^;M&
zcg1>|O@{5EX4`NKGIS^q;toj_*&_H6<87kH#X$1o8DTF3Q^*b@GZs&cw>Brc*wXu5
zanNpN-&fflbS-04P>~r~D*hG6bEQ^*qdEkXddjSRmBOIMn7AW#7%_oCmSt164%p+@^cV`
zXpG?2nQcZJCJeZo5vI_NH(qNo8oFzyc}^h(rjKFD*Zn*-)&uqNjLF*e$4tKLY5;{|3GW^E-6|_L0Juf7{^r(89
z+mq31Klt=BHh`Zkv-VLDqY_G2ge!TYWUuyTfY|Xo;<~b89m__?k9EwM!~Iac1cY>;
zRT1Z`(u1ln(&*d5-0P`f_6UMtQIyh_&-!9!G|vk!M^_}-n-U&%&%9*O-`gNKYD7=|
zwOW6bQ&3T6HILxSJ|j`3H((G*QPJsZsrXgtKJ;l)IFP97bt~Pavlhcr1%2jZHrcA9(?-z8#qs=If0*Y%++^WCtE?jOkERyVhGPLtr$>ztV2~&i|_RSeK
zci2-i=YudNFFa}JqR&8t%bPELklry1>9m>&cf%j~#Te~jzLrjVU(wo+8?88_mP5?2
z#%y2dc?Tah+SvA+tX*Z}kK3NaEvsafR2Gyh2MO|^NJ4?&6}~rc^V=n;ZmN7!weM3=T0mL$Jf5uIOLQ`dKg%I``QC^D=iL$kn6&UAnNJ>IVdlF#0)jFvRC
zPiE2-NLguT8yKu}kuu1u>;w$Tetdz{H&)
zSQ5Pa^}5(pq)byowl-W&*U#eKN(jh1u(L)W$)L|yt->)^)rmH{RL`f@(BI>ZY8hDA
zimY@c;_!#}3tsDu+z+Sc>BGZ(IW~BDBKFmm^N{xJ-RO=u>%+0Zt1xA;7P;sq^z=(W|seR;w_%Na*DU`ESbPhzGhXDmTL
z73LT)B&2D`yGUN_RfLikZbSg_t
zy5ENHvm%(rInEa7&C)@C9&%k*EiOdW=doXE(x^(l#`t!jvzwHQ!@#G^8XVLVc6xAH
zmtMax?7G;POabCPIcX7niD4!Ex(DZ9ysB-aD_72Ocr_)~NwgN450@nMGnx}-b>XQj
zDr?)Lf8%8SK@g0^&HktFHSRb&2Z@99Sw)@BnMxr-G)`0F+GCDvn}537iq64$kSDW~
zsc%zK?$|l4)YjHkAMB+<E`DrUCxd|T~0
z?sG-_Q$oy2cDWl_C#L~dQvNBiQw4QUq-
zi1QQXCn^3yJasUF8qwGEnK7|nMPUE6jjJ*`FH*-(5Edl<_oxE}nGdu3xF@`Km?~Dd
z1RUe2c3s(6LtG}^bbCBpls{~SQgS&p+~Vzwx&4;xMDmZIwD9Dwm);#DfIJH`;~64>
z6)q!3_v-kG@7+RSRdW2FQY+_q(-+8+hBG#@ua)|_eK0({n`}M08+Aw*ceQ}!1wSvc
zt6u{;zB!He3HD2RvnvvXD@4gX0RUP`FvN5=C
z2ziG&`0VVv#fQAJ=mjhyzrc)PRRL1w=OiYk@QvVS39D<&HL)X~a7FlQlZRF|j8)QB
zCVnwN-b^cyWH5Y0^6YMv;4z+9@mV#Gr!J@c5l!N({)^k7zmZh`;57yp+qgBw~B2QQV1w+RF5tY6<^*YCTJlP`X2V9D7kD@}@
zO?dN-iUJHN7G3#}T#
z1@{U+%DN9}t1@ZJ)#?K#%I+Rp6r{7w&s2J14>eQ&szw;En!)YJYqvUuATjN!eudYV
zH1AT!`FDHjDBbRZr4mUoi$odt*7tN}$=agahMU@T)Cx2s`^JD6~&%vR?cm=T)FFQNB=KyTJ_uJAyJb@Q8Dm?)xC
zF~#o$Ec_q1A)b|*2DA{T=DnrfmUoIs&%8lCvg@dMEb@$|z1{^=eP!bNiAQ7vgPruG
zwywh^v}TA3Oc#J}{(L=iU}@Od{riH+m*u+Y+G~b%JC7iU@-hUi(`d@<3m|XQZ3}7d0xVx8
zmv>sH>xpXIyI9vNKyLo~DWTR&k8Dt3uy@YIDD4BYGIs^#M~wiY)(jvG?9%-3VoQ&c
z0kFUSxaIqW}x+SamzSue${xYd`6fv(VrAcd)3rMa_2u6UJL*=v5$R|40@n{2;+9lwXM
zI$vHWNSjrn13ZMI()-#0Q2h|d%)h1Gm`C?(l7r|7Gv_)1?TSo>Sd#k)w^F~U7^^vS
zKOKMZs<(!GU6L_QlWA&AA~hU-wNbZ~a`rCRJw0q%BF*PRg)0V=X9fbje~EIyg2}3@
zN+hjBRqM0@3(>@fYCUJ4B3)2^(0ADj9ifNwZY$@=u&F%Y#jH)7=TD#yVv1*F$k%xX
zkL1}G$-0hSzx_tlOa!u-SK(G@X+ie$NzBtl=BVJ3oVZ0U3nxa66uYfHVglXPk72S~
zYzW_iXz5$STdTQu#oW|d-*?H)gmLnkBp({m$!v-wMBhA#D8#K#O6$~!#=z2(6C<=}
zwbmFij%se$rM6wBxV5mZ>r5F9G=r~3kDmM37Rc4|gI}Uez#{PdQmtLrUPOTKt#k^^
zeuwmhNjUDBpSWs^_ydlyiG@{()!%quH}i&?@B;oxdZ)MzHt0UxP%~gdjyJOPeX$kA
z>YCmBty0NO{ymAWZj*p}owFNY3n$1x9#+MadIofd}xDnsVVFsk4tr%zTX#poRSEGH?D@$n(;8@KQe
zR8yLqZ5N5;WFq5N&d8DxCZS1Zrwu_u)>5ZS(VNak6y0Q}z)5~@&-I@gcP%?;hRgB2
zEdqON^DK=~%1Gs-Fq-Z;VvEA_!1D<-LUc1am+>;$->bV$v!xYBjO-WhDPUh!UGcyE
z%G}{CX(FsS^57d?vxS#Xs!G>_GhV>|P)O&9B{_fQf{8ZV)i^_>6M+ccdF;B(H^b|&Ykcw_AjJz!J6!n#M4OXlVM4eWsrr
zU@_OMvhylHP-hFq2TiuCtULp`Ny5R`d`BAW5VJN`nPC=!$IUNUeR2<)Ey^1eG7Iqq
zHrVHUyJFLTDb7*D2P2ueph}_*#z3`d(LU@xes|TT5_;_XR{^wAq{EJF&V_6TE}Fmz
zO`?ID+Dc%G?F~zUU2)-k@&MI=G*V8Fi4{OeBb2#)sX#xc++MNl-*e;5{ieG~hShkm
z+GTvxMq&2$OrM070~M#vNXp0{{yg&LOi
z&X+YtVeTO4$4@OFY3H%52<6;oR8*ySNpH#dR7MCRTrX-p5NjvAkHd6y1p%MkI@8w9
z{Y1z?;9PD#c7!9h>42K#5v^x9SDYV?KuDi(0#kcDI~9pbM6*cEf~$6Wt&I
z%u~V1ASkmdCw+xiksvk;Io_=>tF-|%BMNKSLz{vFH6g?a5*LQ4Xx|=Mx>$Qju7dI9
zdLar9hqQVW-$Get2He_yEB}s#pzOq`^%O{E;PDYGuP3fM=aJn<%iaa7tSF!l5Hd@O
z*YFjCD&Lt=dMXzEdAJ)7QI!aFXl*7*z#JN~x0FXH4T)P=b!4BkDOYr0DJ&weRuqO0
z_%^n$d*Z1AX)s#4emkmj<};PYEI
zE^jm3@Fx~$(qSe&n
zNd1Cze7I4ktoQU2SdB3fVP{hcLGl4@2;nTo6B~Hw`pB6yCrU
zkyYP0$~TH;-4@Uk?(1fJgG3usZ*ivwu0ER=nHsg7WhhzKy_^KSN?bxzwLOD%aEUwM
zSgOyC*Fe;06Gu_aH#J)rHNNiQTj*yIAWv~0WgiS}bcTw-$WKccyi_5PCO;Vt3oHao
zuiUM1(lGwnhuw>Fr_WC4Y#mXSK8>gjJw&=i<^Lb{S#6a+gFhpla)?AJnL*J94*O3P
z!yPTN>Ux?d_0J!?QiF31S>cLTb-8^(KrX1tDsvRcm^L{gaXh#n8epfA4}GEpNo}p^
z*BRsvRZ95qzdbF+f
zEW-+yw}M
z2N}Rj<)r_5Za^OU!M^P|!Nl~3@fOCRP#@7*RDJUSnXz&
z%g|%XWWr2kym?>;eRINGgw@~IwCDGJh?6@Kp$O>2F2(%V*2feUmFn5KW5H@++Ijk$~>U>m|}5oVUe=8vS{P
z%#JwWl>Nv;NvwNeN#xnotAt6HEroEEY@Z>G=wlauxoG_N0nr_F+VJ=
z&sf}MjKFS(Di{?;y?N605H`r{{xQpJmJ1q1Lm{j}6rSTEfm3^1hEPuPY+Mi*{q)Py
zMbZ;gE>Yu7bsTBgWi?dpBV(~SQ-5QlY(`0i4yN`WkPKu<{uX<^5`
z89FdK1WxjGw=A%VN5cRhGesxj8g5+192(ovUKAA}i6_i4?>e%@9ORC|7^(D$=ZaL-
z*|?DyMutMiRdsmqqyjNO0!*g9lk4)WD33a_I%*KrfZH305$;Gl&ia2z5VC7
zyZ3hMK!V)Z%c+=c1B8#tS?A2?<>-&oWlsHbS>(XU4)~Mwu1;0udwF-*X9sI?j-@UL
z93F03oCHZ7yoO&GkR*j4P(CS@yF4o4Pe80(0Z2)^(~#njjl{1i$%Xb*Fp>+*qZxlCsF%>a2m;<6CZbG<
z9c*53DKKH^YchvR2Jx`cLnLGFZr8zlf!Bln0=Si>IgIgmH(qi|_x}(>rS{6^G
z+)5jF%{I$@^od!~-&ZBQa-ajX9DfnK3xth}HtqdKJl131pC`8aFWQGR&G2(X
z`iTD*mv2M8J9OR9S1o;UYbB4J)_ldT
z^YPn$hTjZbV=j?pr!CC$lOJ%kKiE7}_$B4((vUVg%_UMD-iCm+T*)(y%bt+g6@p=_?ca3R_g@Qm-gY>7K;OaimhqG_;F6gU-;lDQ$8+#hj0)~tPT>fLr^%_1^=^L0)dXw97i
zP_k*fyZeOwQ+9U&_2{cd?=GWG^VJuQCxsEK=MN_JzQ%Aqrh*8Z&BoT^Eo*xXZAbRJ
z`?1NBWAk~)_Kh&?y$@CK=CI$aQ5ZW-i<=IRV97mShW4kNs?;-6S$GRB2hOhzvDt9-Wz`1z|h%o!t?EtFxQ>go0EAiA1iHMqzRWqd5@
z;2+-ee
zzX3hHMzU^-5-ziVYqT##OR%>9f%$e*Sxs`=cYs
z4je(T?zTHYMSxsM7~<pYD!Wg)?pO1dlDD^htVXEC;4TKFVg7=(s
zwZ{`#57#4))cV?;g3Q73oFr^cSKA!-)=m5;fjgw!Eq4r4)iFqQevsdmuCkAl6s`6X
zhNOMi_i9xG*HiI~YkjeP`>@9>`l`*hTkqNrQ^0domu~mHPCv@Luao(yg@_oRdf
z2L~>EO11`|bOwe&Ng8D00VJO7A&pkuZA%1P4zhr2?Z9Esk=>XB=pp%hvsA>oL%6%i
zoQE^Qy*Ut|^TpUUM!&03w7Pzu%caeB8ELIh4#SKbWi|rm`%1Q>!D+dockTtLdQWef
z9nBCuOAKU~ncgL@=0;op!a>rn&OT1hZz=l`O^IbObDalx=YXZznHF~1-b7B4a+Zdy
zda*$y!_r>S!@@=*$UI=>_il`dE5I_PK$re)nLw+nV_40f!O$N^lQv8~fnW1A)sCBW
zM{hGQ_CUUk-K&kt?8YAG&b)5>R;*j+NswFoX8{w^ErAA=P~<=!9bi(z-Uvb2eXuue
zz5;mf4&dR6VL`pVMq8ZRGOX5&Uk3Q)hTFFVh=Dhy?#`AGukT1>M+W))(o?~mPg^7f
zYPb-f@gf1v)5+h===r$onn#>12CeT%YgGdYfapKWG_G0dctMY|=kWiz#nAkHYIod;
ztFtK=ewW(W6%DIp*Z{rIB}yHa;T644ZmO)Uc!}Uk_vsN0Sd6J%?qn=cAkOVR9mujm
zb7oP%R)dij;gdls`Fb=ANZ
zn!xX**K=xzzOH;CHeoH6?cRw=Xp~-L(y-Ez@e_zkT67I~-X>tx>ax;ot6ISGaF8f?
z{>ZD8!`3F68O~=%8DoK^#4}1ono&v@y>x|n++
z@$)8T?{8B)e>l-}XGa&fogu@9n&%{~ZZ+nldYbq4AOMHV8=v_REQgJtlGCN?{A4%h
zH^W4yBu%Mo9D@XnA
zMl4={)tOc4?p5R6t~1LZKn32puAC&wp|Wt{%crX%F~3Bv3IoM%9<2#p7UtCtagaZ)
zE2}39r13o@%2-VkD|GS(dBOp)d4}&oG@g&qZNf{MT)`QwQ^GlGfULHva%3cPsOze8
zN*u8~`?8rSD-Cd6HQcVpFRYq#F9L=mE#xXA8Cum8uZm%IGWJNZd4vz>tE@w0g#ggy7jpau3)5wLGr@q{-T`Jr6TwoLAgL!S6p%K4yUb_Cf
z2tMlZ`u>hJ;naasyQ~S3+ghVHlzbM9#?ZKmiQ?b^q#>NuYal^;$1Ziuug*qs`vHYbaDYH@5AIg}<%H%c84bJeK85h*&I00RaQ?A6LT$*@j~{2&
z0&R#QP?ykSC(TWRT`Nb#0fCHXeZE{emUFzgwyf+cZRY(K*|$h)4UwKu!7K@6at>_=
zp&NWn=dOVLHxtVR4qqBC%0t7x$7op7Khcwb1W5_lt$6@2t!KyZ9L&%5TePc(Ssi9o
zqVEuL$w2aNkzMm&YJoLY5rvnAa65ji$5nGr*!&=r=4p(A?=yr7A~jJe{Sc`^C<7Uv
zBVZ!yIIKFmw9ok~2oeZLIXxv;RX2T;k9&{avg4@oO54B>awIJBUv6Y3M7kXt#{d
z{1{l@=-OVv31)Zg8ZNWvLVc4mSmg1-G`&2C=fV*>iT
zQw2~k%Droo7K^=9`0pV@gc&7P8DSxUA8ci4=UZ42sf}h-g+jwi5RE(ddDRQm8~)2f
zF42RP!KrTPQBV6B_i(yinB~8tI^kf7h#a)10VZ>@c;K2)U7?f){CbXbv0?G&9l6p)
zbW@5nSgWZ2cmdx}7nzQUxZ%|NUE6Ttn;kXR5jx^UPVI@o4)J6x>^U(!8puFpF+N6%
zZScw&qL9?dA~nCc5uKd?V;cwC$Oko(rO}(M?I)}l@+QzjH7prnre3q}J;~7+0qA$!
zgfjIV5K#$d#J4nd@VSy
zvQZCx5+PiQ^ls`Lyx%c&0JO4_2EoM`mkYGAs_?}x{mK({0aSS$z#kYc1g{%xV^&}Y
z?n)mdEqr=ZLVOchTO`GE#9S25X_l;9UCF3$SJ4CH|jWhP;~}(xlDzMf;XC8_#|+kIzAJBuPC7C+Sd2&
zAGmMj`%NN{6tqR_gWQ<+1~mC=xIkAxdi@FIPuMzPB
zT6D2u^y+QX)L+b4-a0`!NSl6=+wQw)-aop1QE;p^U}3Ljq|1atR*wVUdf0q;$+8D=
zhmb&RxpUQNfYY%U^YcT68@5CDXF+gsR0cTRFgDeFM9uk8Ugk?ckgRG?guF3c1t}A@
zB8n4i@r-jh`#AP?N2sa$A9PlZCdAiOZZKx>+!=I+4I^npDr
z4#XAcXu6U^Pmit)8uR+VHfX}9VNB`Kvh-P?4T@_7neab$=1!drByIRu&;qBzMK9kB
zWGwg*7l%|<)+N#d?4C1PwX-d7s*v$eqkEh~_$t?UfIE#z@#>$D($xm*Hi|}HY%hJ3
zu56V1}8Ew+k(9o;(*oLl>HQ{Eo%hMiehn1lRXzhWH=e2@XmoeD0rnp7=B
zwl0N>M3}^z(bXsxc5Es_EZO^J62C!lKs`OU^4`cIzG{K4AckJp!*__Q;}@lp;C%Dy
zk79$;DVGd<=Lg4y(jdGClvi&`V(jN6x4>p|GZe^&1nR;_Vlyqhw5N|tR7u^-H$Q1L
z@KKkNzgcu?k3`Bbyo>Bt0Bg-3aQ85&ZC?bH+1=x`#3d0NIJ8ydSPZ`@K`!7uBKXYYR03*4HdBA1061qux<;v{P+uag=R5vJVTA=?VEyAZ{HnICxa
zcSNDWGY;LG{*AzCAy`s1nBzT^S^C9c1PJ
z)cE;r)%wVZf$qn`q$Syxt4enzlw51j50&-Rm_T6#9UfNQT%2swS89q0%)a-2qc_{k
zZ+*x9lH@B5Tb}N?;=Tp$EqfqP&P0g%&*!>R14#~Gl!GO;b>*`TE@UbVBB}*ZGk<1l
z`&g{r6_bVF{tAtz@3WsU*^}MY&NgUu7kmBm}jWTji6Ckjk!kI!ccTDa>#f+1^S&qtB=}PjH@>Rg5
zUqdUyG-Zp!(>8CXA#+x?f%f;az>gQdW2OCA4|)su5U-;*m&k0)M3?n<
zMF~z)aazBo;h6ySGPVtg{#}G-1N8O3-7)a47cjH7L#_%e96fiD)qiU}qvqKAFNRJ)
zBHP~3$ua5T6JF4$|LI4BT0AU#d@+CY@kMvca$3N~>`a*(VWYZh*2_@OyAW$7DhCWS
zOx_n@0T?oiAPU7B6VswpiD7)&2l#T@ip%UexRwBiqYw|EdZhuarG1$zzKRV$YnF`*
zj(SS*>nkC}&2(VV#ct4Av)k~KKdb&u2Juf!Xe%C
z5##0N92x1gRi1pbGJ%X_TigId^KZDJ
z@x%D2GzN0nd2u>zRk24yE%PJf6GJiit29CiB~cajM(K^O680vr|5R=tKhb9)59!X#
zA_ZN#s_$*JNjkK+=AubeYerpDi^#7|Hiy%Ydd`N}@)=WM6Qi=2fmQnGrQxn}BI+A+
zRf7Y+to3X9TYL
zhbRlFB9eJ^ndnL3A78|x!Vd_!-bRQgBM2x02dP1gji9nL?0&ey4?(?o;FnPJYNc5f
z=#8qwVsk|fkG|bwx`E0NU?JpA?F`H!8$0`sG#grrkqG$rovZkvlQf2sSP1C7J7dtj
z?`fUEEbQ}0Acl&NKG7|?utU9dDkqw=d_7F5+op@`rq*))y9EFDY%~!;12%!IqWd)(bXS)-amG9heCJ><86^WN&r%L
ziV~h0h1Nq-mPVGPZ5I+~$(*CBHLE|TB+DbCb%DgVu0p_
zMbGH03af!->DrVf^Wo6PexDI`LxGtU3o75h_}9|vL=l1K&)6$;*?k~x-S)yOUov6I
z^3)S>LZF{1DLd1dfc0R!83SWLvI|J43jv|$hFSrYOY!P0_z-46{0j%VQJnKyV|)3<
z6D2EF(7k6qipI(n>UUBe{FpNAe6`xtS-mpMLaV>PK@#kqoBUuxjoWGTx<)mDF%~o>
zvGrO_sE@D-%-3Y`GT$F(U;3!Y7@h5r83_l9dwSIizwa$!@PPk;C%9xL+
zxR^!F2veLn>^r<_1ELwn=Qmk5pvcWT17bTn?v6(q%7)^x(3lw9w@g
zC(#Pbmml)Ld=T;gqpc85IHR^j^Zpp6jbZc`Gsnbs>Yb)$?F{ku#}Y%5Gh}s0puKG6
z$q~9QDHIrnP}o*tqg_z^ozEiJRDVaot9!+|OWp?tUidS({7Qq&OS=C)@N)W?n$?#`
zq-9wOFq%~$i!3OeERF@{UbKJ-)H{Ab=6Pus(97o9tB-1y^neM-pONYo>lOW|rX0d~c%cn!K$f&8_^BonmAt6A--KbMgCjwG||8PPU
znK=(scwpvhXm9t*LyjHVX0P)_@QW428S}H!D4LUt6WvD?Van?arg`pYU+UE*2d?~d
zk>1|}&Q(Ei-=J0ouE;4Q@vvPy8f<(D|4JoTT9j`N>Yq%_C+o=U_0oE5fb%L6mS{?6I2Uj+HrXOWxZvoydnrF4T^`
zcv}jVesma8?2Q{0w>dwzp__1ocj4Up1N@8B4Rc@?sr@V|)QETjxa+~7?gDebB>zXM
z->3|oxJAbhV~os=CC{h%57X+OSxd)CnL(1NPK)-M)kn@aR9q*93h2d>kE|a}@GDH*
zGxk=My-fAZb`gckokXa_*IfY*o7T8Y*d$(cbSf}Y^?Yr*PCwQN1t#2LMe2|3Ar&Pa
zmes;pn%6%x3(SYo-@OB=niSQn-hKx3V?O{fUIrUf?C)H;$soE5ap@O5ee8x}x=mVc
zvu`w-B^adCJ^N(G{)D#|EHu~Wd@}sFlDZUtEGaeiuM%r+NUuIZZ3wTAg+xyg^NHG}
zqPOU#2`9{?UAm#&bf5bhv-Xj4O?ZC&&Ggyu2l)@%hATyrvKhe}j)`|v@8n}We1vY99C)jGS^B+mF;aW_qK9xY(L-mY*F86|R)`0i>3+_d`g
z4!vVJX1QA_TI9p;3dkzp_$u-uZdju;4y2>PrWD`hLv02SQ0bV}ir-?n4H11I~_B
zR&cL=-HR0u{ZDF~rbWGg-5OG5s6Yo;E*qgpX_^DNECfX?vN=Gm)$#brP
zE;;<9Bl7C{ZYS@#^HNu2@{V&1)iNwYJP2Hn(U(u%4i7W6e03kw1!JAtFpDR*tF@{M
zV*OoqoAQB+dfae_tmQnwxr?j?hFUuS_(hAo#+G`dNU)my67Tm@9ozJZ#lDQQtp4Y?
zO2Juc&@~kk>=}EtO{x87J4;f2mlrOLy(s%HwqLRCcWT{M!Qiz;oPKD!i5=&t7p!4NIB21lsn+hb-OjWjI*3Z(yKdr*;eh9^Q>B|-
z&D^msqZ)`d>xgBZf9>kd2_B0Z{=-=x;N~^jD9#0&zGjoD?~?*;dz*+SB_bbRkcdQx
zEp4nR+f+Q09{z+8g>F9SVyq%pArTVZzy9lg1Xy#JG-tz`%+cm_2O3wv!p3;=g3J&Ui8)Oo+O<;^|6%)kKtZ#jnPn>z~tc
z)1yF+(=O{|ayD22ysJ@)$u8a%c$m`%7*hWIPAgMKb04FEWnBjhkLYe|6eWv!bM;EpxePysG)@1}#GE&dAIc$lkCYM8e{QO}G1I8u
zp=txzsdIN)B76O$QR`&k~zgmQ}!|jM)YDF8hkS
zh!BXy2|$X96|79!`N*ZuCEdY8jhP&MfN{>m}+H`^RbRNeFE_J^bnN
zNmRqf+#25(k8sP5xZtDVF
zT~dvrPEr_ECncFuo>7*Z!;y$bsi)X$wGsI|Hopi(QJ0wVerMY&z8uLyRPlob-WVnu
z1k`Y5brPkfkDUGgxvpyeg7Yi5T95(jWPNQ*_hf8iMRLw@goY?+0``tK3Z}3w(Nbl3
z9iDSwAOvs^O`7*PG4zD(Z<6162+BhTzVY1<2FtAvbboydcRt-i@SqziQ`q#Uo)89&jI|G%tWN
zEdRL~?mMg-`e;Ts*#LUYo9dpf-J6FCt+RC}4=WjM2Lhfo5b#zual(H0T}2sVp>%cR
z+M-#}44Gf`Gl${^u!-#q&DU4{ZT12G?lmCE{>O9<_7;f-hhe!sEzHA9tU-u3crg_!$I1-sP
z0a+}_4I^dC^~JBcw)`&O53E`PH}&FX-
zOonZ$+mypbPG;O~Ahq~zl6z%SbjRj58P<9TMuii%k8JPKYz^K1f8`F)$@%E2`9Jj6
zIXyNGojR9n42w2TE!Q7eiWqU+KwI?h=zTphWat#AJiX9r1!gC7*cK?6t=P#KS_vdw=`0115x9IdC}k
zLbWh06ueZkb{JlG%9dc|u5tN}qNS)qZKXz*jct9h6-bs{%_=c+`*2UZ@G+CWo7+O!
zn0U`#M%x=%wu=Qa11viC+d`dxtN6&F#aI7l*ZSRVlIh+G?Ar2QgC)_0NvXV^(?L0U
zd&)a8KY;BzJKC}8lOY&p*Hk6W!DbLjF>dXoXm64s$|n%1gsrXJh)UjrLmMV*!QvT4
zJcc9ba#v2{K+*%$m~oQwZJpYTBxRmamDQn$5J<%NL%ca(%KM69F(-}ChlTA9d4i9h
zGhWZ;QS?y2!Wrj?hQrArs!`r7QKJZNdiLd}mj{=BzP)qfLt4P@R@)7lTif+>HC1hI
zT(~lo7O)=_lorBjhcCc=A~-LMtZwqdKz^^%MFpPkqhx-!6qax(vRJ-Kro=&dr^x_^V>
zmeYT2`hTxq?7dwWV)iA34l-^ukcZCeh%*}O@->Ip&p5+!2PqJ2O`9>asK4qaxB)*D
z>RO^QZk2qOVZ(;CHdw+{=D&^0`Ca*;9DZV=V&`$E?-OG5$lw&G~m
z;L(AFgQ}BLV)U!1CybU)T!}CD?^BAjqop><4C(F2JB+q3*Yg_H&7KK=9)NhZw&ng`
zTkidF-2xZN9L{8XJ$et=LGORRKp-~qgJ6j*781dv7`EzU(`wB`lUU8s!2@rs*H!!CLlF!1c(>PJ
zVvC*0_;j&FHBnD(&6>aC;(78i6r{LC@czljtFdjgVCw#E%$Op%Y=I{
zxJ@S8rgzFUHZ~shd|2d@GE-B>6%^&)NX~ci554)c-)9sKCTFE_^d3|$85tl3-40$_
zI<@#57~g-Nf~Mi%E!(EoN>=WF``>^4*N(mm=ZXAz&S(#1UIQBI@oP%!PNkzZ)dNSo|Mx2XN096$38+QaqQz?j%~G
zNNCP#PCq4KD0p5we4|z%05^q$b*K7eo4^VFbl(z
zE4EUL8Vfa{!2>^EG=_i1wVsErMN{gjd!?*SXy`u08#DC0k8>qbNS}75v>aKDnS8re
z|87Lm>a;!iPL8nwQZdHf5DjebKm`Cc+Q-D4VWZv(jhc!*-_V
z4^CYN4-8_#uV2aWW#jxHt$GCPHlg=!9@QH#|gURaS
zHIsIV<~A)s(W;XpCTOc;qXur#s(VP7XfVWRp2A_#Q5E@f&6}DF+LG$)m)#v$vvWXGOLhAZ)v|E^z1K6JxFu^5O`8f
zIXyZyUXc=;v|PO9+IW+^1z2UN0K=C$KRO8C>kQoiYL)ENIIVCz?^Pi|7aI0?AMW5`
zv(HT{>#;E_l)RFm?{|d#!A{QL$<TS3Pmp~Wlwgb
z9-3UGmVBY-bV>i9EM%{Q)rT4q+OEeprGE}$FZ5vDz@FWl6Q168%h;Z-=1ebJEHeHd
z!ggO=)P{}cz(*06{x53(wW$)+6)k8Ix9)<
zo-DZg7KVh}Kma9^e^nq9S!>S1-&L1iEp10tSS+Nk>)C4FcAYeTw>4JM0>w&Qgt6CS
zU#Cut{Zwe-WL*1f)4cbKMRbP$SXNe0~SOt
zoW;tm#OoBbI)l!HXG?N2_XpfB6y_EASk=mhb1eK9-VRO-EQ%CKIY?#RbrEglAz;S4
zelnpjPFS4Q$2ubG|9!Ow-G5&H^OGb;=iAPZL(Z0`u@{SDZ;qA`IRC1*L-`I_oP%1}T8p&9-Qh0z*Z;*=`#N{xFXf~MYk65bJ1CR4&y
z-1^xylRs?;2``;!@!tpuK^pU#`%fZU@zp%UfjTtYGj6n`_7>XimerdFkRL1X`7&w`
zq(%Q0hPJMjSyTVx(`sZ-m7lVrKP_aGDCf8KY4~X2^6^H-HDjT5TP)0Xgav<8^Vkt#
zx{$JB(X?XMtACgNK^7#$9AKLzkGOoQ@S=6B<=uL_v|rZHA~)~TjuCL+2fD*@{Du{iu17H4^}xQ2RmmtK|NnA$=Qawrm2n#PsJoQ
z3Tw5VNp|ZlILwE18*6u}EfMOY?Qd8gGBk6MDtDuCXWc+cKO}10sH)+vRFWTM-oB^C
zs7yB)no+4h83aD;Acap<3;N8Od0p_jBtU&`t!A#I&xq9m^`B&--D=kag+J`E^ze1G93S=%1M#Lifsj**DBK0oMvAEe(eNx`{$6mElT
z=I}#rVKS3B50OtD0_Iq6$SThZ9zvR76eWtJ6PCka#l(#`!L6UzXcy_I&szG3-AS!1
z4(RhCrRvO_a;bJw#>I%P?cNLWtuvob;mh$0*}X##!FDS
zL9q@bOY | |