aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml6
-rw-r--r--SECURITY.md1
-rwxr-xr-xcontrib/seeds/makeseeds.py7
-rw-r--r--contrib/seeds/nodes_main.txt1502
-rw-r--r--contrib/seeds/nodes_main_manual.txt77
-rw-r--r--doc/bitcoin-conf.md2
-rw-r--r--doc/descriptors.md7
-rw-r--r--doc/i2p.md26
-rw-r--r--doc/policy/packages.md2
-rw-r--r--doc/release-notes-25355.md8
-rw-r--r--src/.clang-tidy2
-rw-r--r--src/Makefile.am3
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/bitcoin-chainstate.cpp7
-rw-r--r--src/blockfilter.cpp16
-rw-r--r--src/chain.cpp47
-rw-r--r--src/chain.h16
-rw-r--r--src/chainparamsseeds.h1078
-rw-r--r--src/consensus/params.h5
-rw-r--r--src/consensus/validation.h1
-rw-r--r--src/headerssync.cpp317
-rw-r--r--src/headerssync.h277
-rw-r--r--src/i2p.cpp63
-rw-r--r--src/i2p.h19
-rw-r--r--src/init.cpp2
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/kernel/chainstatemanager_opts.h4
-rw-r--r--src/logging.cpp3
-rw-r--r--src/logging.h1
-rw-r--r--src/net.cpp64
-rw-r--r--src/net.h45
-rw-r--r--src/net_processing.cpp473
-rw-r--r--src/net_processing.h1
-rw-r--r--src/node/interface_ui.cpp2
-rw-r--r--src/node/interface_ui.h2
-rw-r--r--src/node/interfaces.cpp10
-rw-r--r--src/node/mempool_args.cpp2
-rw-r--r--src/node/miner.cpp4
-rw-r--r--src/policy/policy.h2
-rw-r--r--src/policy/rbf.cpp12
-rw-r--r--src/policy/rbf.h30
-rw-r--r--src/pow.cpp51
-rw-r--r--src/pow.h14
-rw-r--r--src/primitives/block.h6
-rw-r--r--src/protocol.cpp7
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/bitcoingui.cpp26
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/clientmodel.cpp18
-rw-r--r--src/qt/clientmodel.h10
-rw-r--r--src/qt/modaloverlay.cpp12
-rw-r--r--src/qt/modaloverlay.h3
-rw-r--r--src/qt/rpcconsole.cpp6
-rw-r--r--src/qt/rpcconsole.h4
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/sendcoinsdialog.h4
-rw-r--r--src/rpc/mempool.cpp4
-rw-r--r--src/rpc/mining.cpp6
-rw-r--r--src/rpc/net.cpp4
-rw-r--r--src/script/interpreter.h4
-rw-r--r--src/script/sign.cpp5
-rw-r--r--src/script/sign.h2
-rw-r--r--src/test/blockfilter_index_tests.cpp8
-rw-r--r--src/test/coinstatsindex_tests.cpp2
-rw-r--r--src/test/descriptor_tests.cpp95
-rw-r--r--src/test/fuzz/bitdeque.cpp542
-rw-r--r--src/test/fuzz/pow.cpp37
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp2
-rw-r--r--src/test/headers_sync_chainwork_tests.cpp146
-rw-r--r--src/test/i2p_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/pow_tests.cpp27
-rw-r--r--src/test/skiplist_tests.cpp2
-rw-r--r--src/test/util/mining.cpp2
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/test/util_tests.cpp19
-rw-r--r--src/test/validation_block_tests.cpp10
-rw-r--r--src/test/validation_chainstate_tests.cpp2
-rw-r--r--src/timedata.cpp4
-rw-r--r--src/timedata.h2
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util/bitdeque.h469
-rw-r--r--src/util/string.h36
-rw-r--r--src/validation.cpp105
-rw-r--r--src/validation.h53
-rw-r--r--src/wallet/coinselection.cpp59
-rw-r--r--src/wallet/coinselection.h46
-rw-r--r--src/wallet/feebumper.cpp98
-rw-r--r--src/wallet/feebumper.h66
-rw-r--r--src/wallet/interfaces.cpp2
-rw-r--r--src/wallet/load.cpp2
-rw-r--r--src/wallet/rpc/backup.cpp8
-rw-r--r--src/wallet/rpc/spend.cpp6
-rw-r--r--src/wallet/rpc/transactions.cpp4
-rw-r--r--src/wallet/spend.cpp117
-rw-r--r--src/wallet/test/coinselector_tests.cpp13
-rw-r--r--src/wallet/test/feebumper_tests.cpp54
-rw-r--r--src/wallet/test/fuzz/coinselection.cpp8
-rw-r--r--src/wallet/wallet.cpp1
-rw-r--r--src/wallet/wallet.h4
-rwxr-xr-xtest/functional/feature_block.py2
-rwxr-xr-xtest/functional/feature_rbf.py4
-rwxr-xr-xtest/functional/p2p_compactblocks.py24
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py3
-rwxr-xr-xtest/functional/p2p_headers_sync_with_minchainwork.py144
-rwxr-xr-xtest/functional/p2p_i2p_sessions.py36
-rwxr-xr-xtest/functional/p2p_unrequested_blocks.py14
-rwxr-xr-xtest/functional/rpc_blockchain.py11
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py4
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_abandonconflict.py3
-rwxr-xr-xtest/functional/wallet_balance.py3
-rwxr-xr-xtest/functional/wallet_basic.py12
-rwxr-xr-xtest/functional/wallet_bumpfee.py26
-rwxr-xr-xtest/functional/wallet_groups.py6
-rwxr-xr-xtest/functional/wallet_hd.py4
-rwxr-xr-xtest/functional/wallet_importdescriptors.py3
-rwxr-xr-xtest/functional/wallet_listreceivedby.py2
-rwxr-xr-xtest/functional/wallet_listsinceblock.py2
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py12
-rwxr-xr-xtest/functional/wallet_transactiontime_rescan.py7
121 files changed, 5049 insertions, 1680 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index f2a08cdd48..ae962f2906 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -97,8 +97,8 @@ task:
name: "Win64 native [vs2022]"
<< : *FILTER_TEMPLATE
windows_container:
- cpu: 4
- memory: 8G
+ cpu: 6
+ memory: 12G
image: cirrusci/windowsservercore:visualstudio2022
timeout_in: 120m
env:
@@ -162,7 +162,7 @@ task:
ccache_cache:
folder: '%CCACHE_DIR%'
install_tools_script:
- - choco install --yes --no-progress ccache
+ - choco install --yes --no-progress ccache --version=4.6.1
- choco install --yes --no-progress python3 --version=3.9.6
- pip install zmq
- ccache --version
diff --git a/SECURITY.md b/SECURITY.md
index c6a496edf1..c0660e7042 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -15,5 +15,6 @@ The following keys may be used to communicate sensitive information to developer
|------|-------------|
| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
| Michael Ford | E777 299F C265 DD04 7930 70EB 944D 35F9 AC3D B76A |
+| Andrew Chow | 1528 1230 0785 C964 44D3 334D 1756 5732 E08E 5E41 |
You can import a key by running the following command with that individual’s fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.
diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py
index 37c6f5fd7c..eda58c370f 100755
--- a/contrib/seeds/makeseeds.py
+++ b/contrib/seeds/makeseeds.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2013-2020 The Bitcoin Core developers
+# Copyright (c) 2013-2022 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
@@ -7,10 +7,10 @@
#
import argparse
+import collections
import ipaddress
import re
import sys
-import collections
from typing import List, Dict, Union
from asmap import ASMap, net_to_prefix
@@ -38,7 +38,8 @@ PATTERN_AGENT = re.compile(
r"0.20.(0|1|2|99)|"
r"0.21.(0|1|2|99)|"
r"22.(0|99)|"
- r"23.99"
+ r"23.(0|99)|"
+ r"24.99"
r")")
def parseline(line: str) -> Union[dict, None]:
diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt
index d8e34bdb60..f8572b26c7 100644
--- a/contrib/seeds/nodes_main.txt
+++ b/contrib/seeds/nodes_main.txt
@@ -1,688 +1,856 @@
-2.37.30.144:8777
-2.138.174.158:8333
-2.152.78.124:8333
-5.8.18.154:8333
-5.45.74.50:8333
-5.79.123.3:8333
-5.102.168.217:22220
-5.103.137.146:9333
-5.128.87.126:8333
-5.172.132.200:8333
-5.188.62.18:8333
-5.254.101.226:8334
-8.210.18.56:8333
-8.210.92.32:8333
-14.13.34.225:16181
-14.39.151.167:8333
-18.196.79.108:8333
-18.218.139.58:48333
-20.184.15.116:8433
-23.175.0.220:8333
-23.233.107.21:8333
-24.35.68.229:8333
-24.37.3.26:8333
-24.102.91.203:8333
-24.116.153.115:8333
-24.134.6.165:8333
-24.155.218.13:8333
-24.160.137.173:8333
-24.177.106.85:8333
-24.184.0.146:8333
-24.194.222.116:8333
-24.205.215.192:8333
-27.124.108.19:8333
-31.14.40.64:8333
-31.47.202.112:8333
-31.165.115.7:8333
-34.65.45.157:8333
-34.78.48.104:8333
-34.80.134.68:8333
-34.101.132.198:8333
-34.227.68.216:8333
-35.137.212.22:8333
-35.231.190.134:8333
-37.1.217.35:8333
-37.15.62.32:8333
-37.143.118.174:8333
-37.200.59.67:8333
-37.205.9.165:8333
-38.23.180.228:8333
-38.65.119.26:8333
-38.141.134.140:8333
-39.109.122.127:8444
-41.79.70.146:8333
-41.193.122.191:8333
-43.225.62.107:8333
-45.35.73.152:8333
-45.43.97.103:8333
-45.63.10.52:20008
-45.84.153.40:8333
-45.95.64.225:8333
-45.129.180.214:8333
-45.154.255.162:8333
-45.226.80.102:8333
-46.6.10.230:8333
-46.23.87.218:8333
-46.32.50.98:8333
-46.47.84.85:8333
-46.48.126.58:8333
-46.146.248.89:8333
-46.165.221.209:9333
-46.166.142.2:8333
-46.166.162.45:20001
-46.173.50.58:8333
-46.175.178.3:8333
-46.188.30.118:8333
-46.219.120.59:3673
-46.229.238.187:8333
-47.93.230.171:8333
-47.100.162.210:18332
-47.144.106.249:8333
-47.188.70.205:8333
-47.227.226.242:8333
-50.2.13.164:8333
-50.5.46.195:8333
-50.45.128.28:8333
-51.148.153.60:8333
-51.154.62.103:8333
-51.154.131.18:8333
-51.158.150.155:8333
-51.159.2.218:8333
-54.198.19.34:8333
-58.105.168.41:8333
-58.158.0.86:8333
-60.251.129.61:8336
-61.239.91.250:8333
-62.28.190.194:8333
-62.152.58.16:9421
-62.171.129.32:8333
-62.251.54.163:8333
-63.247.147.166:8333
-64.33.68.176:8333
-64.156.192.61:8333
-64.187.175.226:8333
-64.233.245.39:8333
-64.237.82.149:8333
-65.101.247.26:8333
-66.29.129.218:8333
-66.49.204.11:8333
-66.58.243.215:8333
-66.85.234.129:8333
-66.130.120.52:8333
-67.10.121.145:8333
-67.210.228.203:8333
-67.213.87.21:8333
-68.181.4.12:8333
-69.7.124.146:8333
-69.8.175.201:8333
-69.59.18.22:8333
-69.119.193.9:8333
-69.130.201.27:8333
-69.131.101.176:8333
-70.15.194.32:8333
-70.64.27.12:8333
-72.29.170.151:8333
-72.74.34.99:8333
-72.133.177.119:8333
-73.166.84.222:8333
-74.67.240.204:8333
-74.91.115.229:8333
-74.118.137.119:8333
-74.213.251.203:8333
-74.220.255.190:8333
-76.11.60.155:8333
-76.66.144.127:8333
-77.70.16.245:8333
-77.85.204.149:8333
-77.105.87.97:8333
-77.120.113.69:8433
-77.120.113.71:8433
-77.120.122.116:8433
-77.120.122.118:8433
-77.162.190.90:8333
-77.167.245.239:55544
-77.232.41.189:8333
-78.20.227.249:8333
-78.21.167.8:8333
-78.27.139.13:8333
-78.43.208.25:8333
-78.63.28.146:8333
-78.72.228.239:8333
-78.108.102.8:8333
-78.129.0.39:8333
-78.129.169.69:8333
-79.77.182.180:8333
-79.77.182.183:8333
-79.107.178.59:8333
-80.55.225.158:8333
-80.64.211.102:8333
-80.64.211.103:8333
-80.71.57.50:8333
-80.81.3.27:8333
-80.82.55.43:8333
-80.88.172.227:64264
-80.89.203.172:8001
-80.93.213.246:8333
-80.147.82.165:8333
-80.229.28.60:8333
-80.247.233.40:8333
-80.255.8.93:8333
-81.7.17.202:8333
-81.10.241.165:8333
-81.21.86.157:8333
-81.171.22.143:8333
-81.237.206.224:8343
-82.69.23.195:8333
-82.96.96.40:8333
-82.116.50.101:8333
-82.136.99.122:8333
-82.149.97.25:17567
-82.154.24.209:8333
-82.165.241.50:8333
-82.197.218.253:8333
-82.202.68.231:8333
-83.137.41.10:8333
-83.208.6.211:8333
-83.217.8.31:44420
-83.220.110.48:8333
-83.222.138.85:8333
-83.243.191.199:8333
-84.22.139.57:8333
-84.27.155.17:8333
-84.75.28.247:8333
-84.112.60.16:8333
-84.211.7.56:8333
-84.237.7.249:8333
-85.23.51.177:8333
-85.24.145.198:8333
-85.184.138.108:8333
-85.194.238.134:8333
-85.195.54.110:8333
-85.208.71.36:8333
-85.208.71.39:8333
-85.214.136.45:8333
-85.214.161.252:8333
-85.227.245.128:8333
-86.18.34.243:8333
-86.20.50.170:8333
-86.49.105.90:8333
-86.76.7.132:8333
-86.100.26.188:8333
-86.106.143.143:55373
-86.120.58.66:8333
-86.133.251.239:8901
-86.149.8.23:8901
-87.78.197.234:8333
-87.120.8.5:20008
-87.121.37.156:8333
-88.82.181.44:8333
-88.87.93.52:1691
-88.98.235.134:8333
-88.136.187.214:8333
-88.147.244.250:8333
-88.148.153.148:8333
-88.212.45.166:8333
-88.212.55.138:8333
-89.38.96.153:9273
-89.47.161.135:8333
-89.88.62.190:8333
-89.158.32.44:8333
-89.163.145.240:8333
-89.163.249.234:3673
-89.176.196.80:8333
-89.216.21.96:8333
-90.84.227.255:8333
-90.146.130.214:8333
-90.250.9.1:8333
-91.93.194.154:8333
-91.106.188.229:8333
-91.126.40.109:8333
-91.137.127.123:8333
-91.147.232.98:8333
-91.152.123.18:8333
-91.178.17.120:8333
-91.204.99.178:8333
-91.223.175.14:8333
-92.42.110.242:8333
-92.53.90.84:8333
-92.221.155.228:8333
-93.57.81.162:8333
-93.95.88.13:8333
-93.103.13.1:8333
-93.123.180.164:8333
-93.190.117.26:8333
-94.105.125.240:8333
-94.110.23.215:8333
-94.154.159.99:8333
-94.189.161.119:8333
-94.203.255.70:8333
-94.232.173.93:8333
-95.79.122.99:8333
-95.80.1.110:8333
-95.83.73.31:8333
-95.110.133.223:8333
-95.110.234.93:8333
-95.164.65.194:8333
-95.165.8.182:8333
-95.174.219.101:8333
-95.191.130.100:8333
-95.214.53.154:8333
-95.215.205.180:8333
-96.43.130.234:8333
-98.25.201.31:8333
-98.128.247.182:8333
-98.171.21.129:8333
-99.147.135.161:8333
-101.100.163.118:8327
-102.132.245.16:8333
-102.182.204.96:8333
-102.182.235.245:8333
-103.14.245.250:8333
-103.47.192.15:8333
-103.84.84.250:8335
-103.99.168.130:8333
-103.99.168.140:8333
-103.198.192.14:20008
-103.232.104.227:8333
-104.143.2.195:8333
-104.172.235.227:8333
-104.238.220.199:8333
-107.11.115.68:8333
-107.173.166.43:8333
-108.4.212.83:8333
-109.136.73.97:8333
-109.173.98.23:8333
-109.190.68.116:8333
-109.235.246.60:8333
-109.248.206.13:8333
-110.12.64.96:8333
-111.90.140.46:8333
-111.90.159.184:50001
-113.107.201.131:8333
-115.47.141.250:8885
-116.58.171.67:8333
-116.87.57.218:8333
-116.202.161.56:8333
-117.51.159.130:8333
-118.103.126.140:28333
-121.45.190.210:8333
-121.99.193.25:8333
-122.112.148.153:8339
-122.148.135.234:8333
-128.0.190.26:8333
-128.65.194.136:8333
-129.126.172.115:8333
-129.226.125.10:8333
-131.188.40.191:8333
-134.195.185.52:8333
-135.180.44.61:8333
-136.52.114.123:8333
-136.56.170.96:8333
-137.116.213.143:8333
-137.226.34.46:8333
-138.43.233.57:8333
-139.130.41.82:8333
-140.190.12.129:8333
-142.4.105.77:8333
-142.54.181.218:8333
-143.177.231.247:8333
-143.178.64.10:8333
-144.34.161.65:18333
-146.4.124.134:8333
-146.83.56.69:8333
-146.90.193.68:8333
-146.196.55.156:28833
-148.66.50.50:8335
-148.251.1.20:8343
-151.48.95.212:8333
-151.252.193.245:8333
-152.44.137.83:8333
-152.115.191.196:8333
-154.221.31.86:8333
-156.17.103.2:8088
-157.138.20.22:8333
-158.58.188.37:8333
-158.140.209.79:8333
-159.89.230.128:8333
-159.246.25.52:8333
-160.20.59.250:8433
-162.0.234.190:8333
-162.62.26.218:8333
-162.250.188.194:8333
-162.251.70.82:8333
-163.158.206.255:8333
-164.68.105.105:8333
-165.228.174.117:8333
-166.62.82.103:32771
-166.70.49.26:8333
-166.78.241.9:8333
-166.78.241.25:8333
-167.71.73.244:8333
-167.179.147.155:8333
-168.91.238.8:8333
-172.105.21.216:8333
-172.117.105.95:8333
-173.23.103.30:8000
-173.205.92.151:54805
-173.205.92.154:54805
-173.205.92.157:54805
-173.208.152.218:8333
-173.241.227.243:8333
-174.3.4.232:8333
-174.17.11.22:8333
-174.88.241.167:8333
-174.114.102.41:8333
-174.114.250.86:8333
-174.142.191.136:8333
-175.39.72.87:8333
-176.12.16.135:8333
-176.37.23.30:8333
-176.62.179.221:8333
-176.74.136.237:8333
-176.99.6.226:8333
-176.212.185.153:8333
-177.81.236.117:8333
-178.19.106.26:8333
-178.21.118.178:8333
-178.33.232.69:8333
-178.79.84.139:8333
-178.124.162.209:8333
-178.132.2.246:8333
-178.150.96.46:8333
-178.162.212.44:8333
-178.193.226.120:8333
-178.236.137.63:8333
-180.150.46.187:8333
-181.164.210.228:8530
-183.110.220.210:30301
-184.95.58.166:8336
-184.164.147.82:41333
-184.171.208.109:8333
-185.17.143.220:8333
-185.21.217.49:8333
-185.25.48.184:8333
-185.28.96.16:8333
-185.31.136.246:8333
-185.64.116.15:8333
-185.68.249.91:8333
-185.108.247.190:8333
-185.141.60.36:8333
-185.148.3.227:8333
-185.148.145.74:8333
-185.159.20.143:8333
-185.167.113.59:8333
-185.185.26.141:8111
-185.189.132.178:57780
-185.204.197.112:8333
-185.209.70.17:8333
-185.220.156.193:8333
-185.238.129.113:8333
-185.239.221.5:8333
-185.244.217.39:8333
-185.254.97.164:8333
-186.33.167.11:8333
-188.32.14.31:8334
-188.42.40.234:18333
-188.134.8.36:8333
-188.138.88.14:8333
-188.156.110.239:8333
-188.165.244.143:8333
-188.213.68.38:8333
-188.214.129.65:20012
-188.242.15.74:8333
-188.244.4.78:8333
-189.39.6.82:8333
-189.207.46.32:8333
-189.212.121.74:8333
-192.3.11.20:8333
-192.65.170.15:8333
-192.146.137.44:8333
-192.182.157.119:8333
-192.187.109.141:8333
-192.227.80.83:8333
-193.10.203.23:8334
-193.32.127.160:58477
-193.32.127.162:58477
-193.58.196.212:8333
-193.106.29.106:8333
-193.138.154.43:8333
-193.178.170.232:8333
-193.196.37.62:8333
-193.222.130.14:8333
-193.234.50.227:8333
-194.14.246.205:8333
-194.135.135.69:8333
-194.147.113.201:8333
-194.165.30.20:8333
-194.219.62.23:8333
-195.56.63.4:8333
-195.134.183.188:8333
-195.208.103.30:8444
-195.208.103.31:8444
-198.1.231.6:8333
-198.12.14.136:8333
-198.84.237.70:8333
-198.178.120.5:8112
-199.48.92.184:8333
-199.68.199.19:8333
-199.182.184.204:8333
-199.189.242.141:8333
-199.247.7.208:8333
-200.122.181.37:8333
-201.191.6.103:8333
-202.107.219.130:8333
-202.108.211.135:8333
-203.94.33.112:8333
-203.130.48.117:8885
-203.132.94.196:8333
-203.162.13.181:8332
-204.191.201.43:8333
-204.229.10.90:8333
-205.178.41.124:8333
-206.55.178.157:8333
-206.126.203.8:8333
-206.174.115.96:8333
-206.223.153.52:8333
-207.188.159.25:8333
-207.229.46.80:8333
-209.58.145.157:8333
-209.126.81.147:8333
-209.145.63.150:8333
-209.209.10.30:8333
-209.237.127.227:8333
-212.99.226.36:9020
-212.185.86.84:8333
-212.227.211.87:8333
-213.5.36.58:8333
-213.89.236.219:8333
-213.93.145.183:8333
-213.214.66.182:8333
-216.41.249.178:8333
-216.146.251.8:8333
-216.249.70.22:8333
-217.11.240.4:8333
-217.15.178.7:8333
-217.24.233.116:8333
-217.64.148.98:51401
-217.113.121.169:8333
-217.170.124.170:8333
-220.132.135.54:8333
-220.221.58.25:8333
-220.233.178.199:8333
-221.219.97.105:2001
-[2001:1608:1b:f9::1]:26491
-[2001:1620:510::2]:8333
-[2001:1bc0:c1::2000]:8333
-[2001:470:1f0a:89a::2]:8333
-[2001:470:de5a::ec]:9333
-[2001:4b98:dc0:45:216:3eff:fea2:95cd]:8333
-[2001:4dd0:3564:0:fd76:c1d3:1854:5bd9]:8333
-[2001:4de8:b1b2:1:0:dead:beef:7]:8333
-[2001:638:a000:4140::ffff:191]:8333
-[2001:648:2800:131:4b1f:f6fc:20f7:f99f]:8333
-[2001:678:cc8::1:10:88]:20008
-[2001:67c:26b4:ff00::44]:8333
-[2001:67c:2db8:13::92]:8333
-[2001:7c0:2310:0:f816:3eff:fe6c:4f58]:8333
-[2001:818:ea1b:7600:f053:aade:f47b:b701]:8333
-[2001:8f1:1404:3700:8e49:715a:2e09:b634]:9444
-[2001:985:55a0:1::2]:8333
-[2001:999:270:2c2c:c8b:3a20:3f2f:318f]:8333
-[2001:b07:ac9:442b:79d6:bbbe:b37c:a783]:8333
-[2002:2f5b:a5f9::2f5b:a5f9]:8885
-[2002:b6ff:3dca::b6ff:3dca]:28364
-[2400:2410:cea2:d00:41bc:c9ea:861b:51ee]:8333
-[2400:3b00:20:c:bacb:29ff:feab:8886]:8333
-[2401:d002:3902:700:d72c:5e22:4e95:389d]:8333
-[2403:6200:88a0:fb17:f5f2:d8b5:b7ba:f4d3]:8333
-[2405:9800:b910:5f8e:1830:f630:2cc6:88fb]:8333
-[2405:9800:b970:c64c:109f:74e7:ae5f:87c7]:8333
-[2405:aa00:2::40]:8333
-[2407:8800:bc61:2202:d63d:7eff:fe6c:dc36]:8333
-[2408:8248:7004:f831::83c]:8333
-[2409:10:ca20:1df0:224:e8ff:fe1f:60d9]:8333
-[240b:11:43a1:bd00:e589:f8a7:49b:3b86]:8333
-[240d:1a:791:3400:d65d:64ff:fe28:927e]:8333
-[240d:1a:791:3400:d681:d7ff:fef6:a21e]:10050
-[2600:1700:5b2b:5f::8040]:8333
-[2600:2104:1003:c5ab:dc5e:90ff:fe18:1d08]:8333
-[2600:3c00:e002:2e32::1:14]:8333
-[2600:8805:2400:14e:12dd:b1ff:fef2:3013]:8333
-[2602:ffb8::208:72:57:200]:8333
-[2603:301f:1ebf:e000:e23f:49ff:fee7:7431]:8333
-[2603:6081:1800:6600:16dd:a9ff:feee:b2f3]:8333
-[2604:1380:1000:7400::1]:8333
-[2604:4500::2e06]:8112
-[2604:5500:c134:4000:7285:c2ff:fe4a:e143]:32797
-[2604:5500:c134:4000::3fc]:32797
-[2604:7c00:120:4b::eb24]:8333
-[2605:6400:30:f220::]:8333
-[2605:6f80:0:7:fc1b:ccff:fe8a:d822]:8333
-[2605:ae00:203::203]:8333
-[2605:c000:2a0a:1::102]:8333
-[2605:f700:c0:827:225:90ff:fee3:34a6]:8333
-[2607:9280:b:73b:250:56ff:fe14:25b5]:8333
-[2607:f2f8:ad40:bc1::1]:8333
-[2607:fa18:3a01::20]:8333
-[2620:11c:5001:1118:d267:e5ff:fee9:e673]:8333
-[2620:11c:5001:2199:d267:e5ff:fee9:e673]:8333
-[2620:6:2003:105:2d8:61ff:fe0f:853]:8333
-[2620:6e:a000:1:42:42:42:42]:8333
-[2803:cf00:af8:f200:b89e:cf34:92c7:2d26]:8333
-[2804:14c:65d1:402c:bc53:bf5d:68a:2136]:8333
-[2804:7f1:e783:d401:661c:67ff:feba:5547]:8333
-[2804:d57:5537:4800:21e:67ff:fea8:d798]:8333
-[2804:d57:5537:4800:3615:9eff:fe23:d610]:8333
-[2806:2f0:2080:62a:86f:1a01:c44f:1794]:8333
-[2a00:1028:8382:bf22:5f7f:b78f:2737:7739]:8333
-[2a00:12e0:101:99:20c:29ff:fe29:d03f]:8333
-[2a00:1328:e101:c00::163]:8333
-[2a00:1630:10:1003:0:b19:b00b:babe]:8333
-[2a00:1768:2001:27::ef6a]:8333
-[2a00:1828:a004:2::666]:8333
-[2a00:1838:2a:1400:92e2:baff:fe4a:c416]:8333
-[2a00:1c10:2:709::217]:22220
-[2a00:1f40:5001:108:5d17:7703:b0f5:4133]:8333
-[2a00:6020:15dd:ee00:c8c2:2c77:1749:35db]:8333
-[2a00:6020:b482:9200:491a:358c:d8f7:1da]:8333
-[2a00:7145:c1:1:ae29:727:2b87:f64]:5141
-[2a00:8a60:e012:a00::21]:8333
-[2a00:a040:100:f3:45a5:ac0:fea3:71e1]:8333
-[2a01:488:2000:9801::d]:8333
-[2a01:490:16:301::2]:8333
-[2a01:5200:6c:6162:7a61:746b:6f2e:736b]:8333
-[2a01:6380:fffe:73:4e3:b3cc:a871:36d1]:8333
-[2a01:7a0:2:137c::3]:8333
-[2a01:7c8:aac9:c9:5054:ff:fedf:ff95]:8333
-[2a01:7c8:d001:1c1:5054:ff:feee:3e1a]:8333
-[2a01:8740:1:ffc5::8c6a]:8333
-[2a01:cb00:d3d:7700:227:eff:fe28:c565]:8333
-[2a01:d0:0:1c::253]:8333
-[2a01:d0:bef2::12]:8333
-[2a01:e0a:9fb:b0e0:54f8:1901:6e83:62c1]:8333
-[2a01:e0a:aa7:c8c0:9679:affa:b6e5:efc7]:8333
-[2a02:13b8:f000:101::a]:8333
-[2a02:168:6328:0:2a8:2cff:fe68:e32c]:8333
-[2a02:2780:9000:70::7]:8333
-[2a02:2e02:3900:5400:a099:e1ff:feb6:d0e]:8333
-[2a02:390:9000:0:aaa1:59ff:fe43:b57b]:8333
-[2a02:58:97:7d20::60]:8333
-[2a02:6d40:305e:601:dea6:32ff:fe44:4b25]:8333
-[2a02:7a01::91:228:45:130]:8333
-[2a02:7aa0:1619::adc:8de0]:8333
-[2a02:7b40:3e4d:998d::1]:8333
-[2a02:7b40:592f:a187::1]:8333
-[2a02:8388:e5c6:d380:201:2eff:fe82:b3cc]:8333
-[2a02:9a0:102::110]:8333
-[2a02:a311:8143:8c00::4]:8353
-[2a02:af8:fab0:808:85:234:145:132]:8333
-[2a02:e00:fff0:506::1]:8444
-[2a02:e00:fff0:506::a]:8444
-[2a02:e98:20:1504::1]:8333
-[2a03:4000:47:f1::1]:8333
-[2a03:6000:870:0:46:23:87:218]:8333
-[2a03:7380:3015:524:afc5:d3bc:7c66:8f94]:8333
-[2a03:ec0:0:928:8c00:93ff:fe84:a007]:8333
-[2a03:ec0:0:928::701]:8333
-[2a04:2180:0:2::aa]:8333
-[2a04:52c0:101:29e::]:8333
-[2a04:52c0:103:c455::1]:8333
-[2a04:bc40:1dc3:8d::2:1001]:8333
-[2a05:1500:702:0:1c00:40ff:fe00:c]:8333
-[2a06:dd00:10:3:225:90ff:fe32:64cc]:8333
-[2a06:dd00:1:22:225:90ff:fe0e:bd48]:8333
-[2a07:6b47:100:464::9357:ffda]:8333
-[2a07:a880:4601:1062:b4b4:bd2a:39d4:7acf]:51401
-[2a07:abc4::1:946]:8333
-[2a07:abc4::89:234:180:194]:8333
-[2a09:2681:102::210]:8333
-[2a0a:c801:1:7::183]:8333
-[2a0b:f300:2:6::2]:8333
-[2a0c:59c0:18::a20e]:57658
-[2a0d:5600:24:a8e::a91e]:55373
-[2a0d:eb00:8005:1::13]:8333
-[2a10:4740:45:1:a013:d1ff:fe85:36e3]:8333
-[2a10:8b40:1::103]:8335
-[2c0f:f8f0:da51:0:70c3:eea9:9717:9579]:8333
+2.3.25.181:8333 # AS3215
+2.152.78.124:8333 # AS12430
+5.39.74.166:8333 # AS16276
+5.45.79.81:18332 # AS50673
+5.53.16.128:8333 # AS50923
+5.95.186.78:8333 # AS30722
+5.128.87.126:8333 # AS31200
+5.133.65.82:8333 # AS15440
+5.146.20.229:8333 # AS3209
+5.180.41.119:8333 # AS18978
+5.188.62.18:8333 # AS34665
+5.199.173.66:8333 # AS16125
+5.255.97.25:8333 # AS60404
+5.255.103.180:8333 # AS60404
+8.209.70.77:8333 # AS45102
+8.209.105.138:8333 # AS45102
+18.162.208.153:48332 # AS16509
+23.175.0.200:8333 # AS395502
+23.175.0.222:8333 # AS395502
+23.233.107.21:8333 # AS5645
+23.236.25.169:8333 # AS30029
+24.35.68.229:8333 # AS11404
+24.84.164.50:8333 # AS6327
+24.116.153.115:8333 # AS11492
+24.184.0.146:8333 # AS6128
+27.33.160.196:8333 # AS7545
+27.124.108.19:8333 # AS58511
+27.148.206.140:8333 # AS4134
+31.17.64.192:8333 # AS204028
+31.18.114.135:8333 # AS204028
+31.41.23.249:8333 # AS31287
+31.42.176.138:8333 # AS43641
+31.47.202.112:8333 # AS34385
+34.65.45.157:8333 # AS15169
+34.80.134.68:8333 # AS15169
+34.126.115.35:8333 # AS396982
+37.1.204.231:8333 # AS50673
+37.120.155.34:8333 # AS9009
+37.143.118.174:8333 # AS48926
+37.193.227.16:8333 # AS31200
+37.220.135.151:8333 # AS41206
+37.235.146.236:8333 # AS41268
+38.124.126.42:8333 # AS11550
+38.141.134.140:8333 # AS174
+38.145.151.150:8333 # AS40545
+40.115.137.28:8333 # AS8075
+41.72.154.66:8333 # AS37153
+41.79.70.146:8333 # AS37349
+42.193.55.135:8333 # AS45090
+43.225.62.107:8333 # AS63953
+45.43.97.103:8333 # AS26827
+45.85.48.58:8333 # AS208016
+45.126.26.229:8333 # AS45763
+45.134.142.40:8333 # AS60068
+45.154.252.162:8333 # AS13335
+46.13.216.169:8333 # AS6855
+46.23.87.218:8333 # AS51088
+46.40.127.164:8333 # AS43205
+46.48.126.58:8333 # AS12668
+46.59.13.35:8333 # AS8473
+46.72.238.17:8333 # AS12714
+46.128.141.184:8333 # AS16097
+46.146.248.89:8333 # AS9049
+46.165.221.209:9333 # AS28753
+46.166.142.2:8333 # AS43350
+46.175.178.3:8333 # AS28725
+47.36.144.51:8333 # AS20115
+47.180.49.158:8333 # AS5650
+49.228.131.133:2210 # AS133481
+50.2.13.164:8333 # AS62904
+50.35.71.51:8333 # AS20055
+50.53.250.162:8333 # AS20055
+51.68.36.57:8333 # AS16276
+51.138.4.135:30001 # AS8075
+51.154.62.103:8333 # AS15796
+51.158.150.155:8333 # AS12876
+54.176.63.16:8333 # AS16509
+58.158.0.86:8333 # AS2519
+59.138.115.137:8333 # AS2516
+59.167.191.60:8333 # AS4739
+60.205.205.119:8333 # AS37963
+60.234.122.245:8333 # AS9790
+60.240.210.155:8333 # AS7545
+61.239.91.250:8333 # AS9269
+62.74.143.11:8333 # AS3329
+62.138.162.12:8333 # AS20773
+62.169.74.233:8333 # AS2860
+62.171.129.32:8333 # AS51167
+62.209.198.65:8333 # AS6855
+63.247.147.166:8333 # AS30221
+64.98.76.62:8333 # AS32133
+66.29.129.218:8333 # AS22612
+66.96.235.28:8333 # AS63859
+66.130.120.52:8333 # AS5769
+66.198.209.243:8333 # AS33152
+66.208.64.128:8333 # AS10352
+66.225.231.148:8333 # AS23352
+67.55.3.200:8333 # AS33139
+67.58.232.107:8333 # AS14051
+67.211.92.2:8333 # AS11711
+67.223.119.122:8333 # AS22612
+68.48.131.251:8333 # AS7922
+68.181.4.12:8333 # AS47
+69.14.185.9:8333 # AS12083
+69.54.29.193:8333 # AS12282
+69.59.18.22:8333 # AS397444
+69.131.101.176:8333 # AS4181
+69.165.205.142:8833 # AS5645
+69.228.219.124:8333 # AS7018
+70.59.123.25:8333 # AS209
+70.62.13.150:8333 # AS7843
+70.66.248.170:8333 # AS6327
+70.112.153.229:8333 # AS7843
+70.160.240.132:8333 # AS22773
+70.190.177.204:8333 # AS22773
+71.28.189.239:8333 # AS398465
+71.234.125.198:8333 # AS1351
+72.74.123.179:8333 # AS701
+72.253.236.217:8333 # AS36149
+73.219.254.120:8333 # AS1351
+74.91.115.229:8333 # AS14586
+74.118.137.119:8333 # AS20326
+74.195.166.100:8333 # AS19108
+74.220.255.190:8333 # AS23175
+76.67.211.110:8333 # AS577
+76.169.163.14:8333 # AS20001
+77.32.121.162:8333 # AS35612
+77.53.135.74:8333 # AS45011
+77.70.16.245:8333 # AS8717
+77.85.204.149:8333 # AS8866
+77.107.38.239:8333 # AS62183
+77.120.26.102:8333 # AS25229
+77.162.190.90:8333 # AS1136
+78.20.227.249:8333 # AS6848
+78.21.167.8:8333 # AS6848
+78.27.139.13:8333 # AS6723
+78.90.91.220:8333 # AS8717
+78.108.108.25:8333 # AS8251
+78.108.108.38:8333 # AS8251
+79.77.182.183:8333 # AS13285
+79.98.159.7:11333 # AS44065
+79.189.211.201:8333 # AS5617
+80.55.225.158:8333 # AS5617
+80.83.186.35:8333 # AS33891
+80.88.172.227:64264 # AS31263
+80.209.87.103:9333 # AS31027
+80.229.28.60:8333 # AS2856
+81.7.16.182:8333 # AS35366
+81.7.17.202:8333 # AS35366
+81.19.10.2:8333 # AS24641
+81.88.221.190:8333 # AS39709
+81.171.22.143:8333 # AS60781
+81.224.44.164:8333 # AS3301
+81.224.160.81:8333 # AS3301
+82.1.68.54:8333 # AS5089
+82.21.164.47:8333 # AS5089
+82.64.116.5:8333 # AS12322
+82.66.10.11:8333 # AS12322
+82.96.96.40:8333 # AS29686
+82.116.50.101:8333 # AS30936
+82.129.68.62:8333 # AS48945
+82.136.99.122:8333 # AS8821
+82.154.24.209:8333 # AS8657
+82.197.215.125:8333 # AS25596
+83.128.132.91:8333 # AS15435
+83.137.41.10:8333 # AS31394
+83.208.6.211:8333 # AS5610
+83.208.193.242:8333 # AS5610
+83.222.138.85:8333 # AS31736
+83.240.124.68:8333 # AS31246
+83.243.191.199:8333 # AS41164
+84.9.5.211:8333 # AS5378
+84.28.57.90:8333 # AS6830
+84.38.3.249:8333 # AS196691
+84.112.60.16:8333 # AS8412
+84.215.56.119:8333 # AS41164
+84.226.243.175:8333 # AS6730
+84.245.14.73:8333 # AS25596
+84.252.157.90:18333 # AS200590
+84.255.244.61:8333 # AS34779
+85.23.24.123:8333 # AS16086
+85.52.185.29:8666 # AS12479
+85.58.120.201:8333 # AS12479
+85.93.96.18:8333 # AS29208
+85.165.8.197:8333 # AS2119
+85.173.165.66:8333 # AS12389
+85.184.143.105:8333 # AS39642
+85.191.74.103:8333 # AS39642
+85.194.238.134:8333 # AS47605
+85.195.54.110:8333 # AS35706
+85.195.196.142:8333 # AS13030
+85.208.69.11:8333 # AS25091
+85.208.69.21:8333 # AS25091
+85.208.71.36:8333 # AS42275
+85.208.71.39:8333 # AS42275
+85.214.118.71:8333 # AS6724
+85.214.161.252:8333 # AS6724
+85.216.32.73:8333 # AS51185
+85.254.98.221:8333 # AS13194
+86.58.11.152:8333 # AS3212
+86.95.8.249:8333 # AS1136
+86.100.26.188:8333 # AS39007
+86.106.143.143:55373 # AS9009
+86.124.145.184:8333 # AS8708
+86.133.251.239:8901 # AS2856
+87.79.94.221:8333 # AS8422
+87.120.8.5:20008 # AS34224
+87.125.157.220:8333 # AS12430
+88.9.76.133:8333 # AS3352
+88.90.184.68:8333 # AS2119
+88.151.101.14:5000 # AS41075
+88.151.101.253:5000 # AS41075
+88.198.92.47:8333 # AS24940
+88.208.115.70:8333 # AS29208
+88.210.15.24:8333 # AS212702
+88.212.45.166:8333 # AS42841
+89.102.206.238:8333 # AS16019
+89.103.111.34:8333 # AS16019
+89.114.143.113:8333 # AS12353
+89.134.62.74:8333 # AS21334
+89.152.8.231:8333 # AS2860
+89.161.26.78:8333 # AS39375
+89.207.131.19:8333 # AS49544
+89.248.193.229:8333 # AS49505
+90.3.48.62:8333 # AS3215
+90.146.121.97:8333 # AS12605
+90.146.130.214:8333 # AS12605
+90.196.169.58:8333 # AS5607
+90.250.9.1:8333 # AS5378
+91.93.194.154:8333 # AS34984
+91.126.40.109:8333 # AS35699
+91.204.99.178:8333 # AS20485
+91.204.149.5:8333 # AS42765
+91.206.17.195:8333 # AS13259
+91.209.51.131:8333 # AS48239
+91.215.91.254:8333 # AS48078
+92.91.27.60:8333 # AS15557
+92.221.20.232:8333 # AS29695
+92.255.85.31:8333 # AS9002
+93.4.101.37:8333 # AS15557
+93.46.81.5:8333 # AS12874
+93.57.81.162:8333 # AS12874
+93.73.39.196:8333 # AS25229
+93.90.82.226:8333 # AS47626
+93.95.88.13:8333 # AS35434
+93.123.180.164:8333 # AS35539
+93.189.145.169:8333 # AS12555
+94.17.185.107:8333 # AS12709
+94.75.198.120:8333 # AS60781
+94.114.196.169:8333 # AS3209
+94.142.213.250:55544 # AS5524
+94.154.159.99:8333 # AS62240
+94.158.246.183:8333 # AS39798
+94.239.145.32:8333 # AS5410
+95.31.12.22:8333 # AS8402
+95.31.196.15:8333 # AS3216
+95.110.133.223:8333 # AS31034
+95.110.234.93:8333 # AS31034
+95.161.12.45:8333 # AS39598
+95.191.130.100:8333 # AS12389
+95.208.158.161:8333 # AS51185
+95.213.145.218:8333 # AS49505
+95.214.53.154:8333 # AS201814
+95.214.53.160:8333 # AS201814
+96.44.156.199:8333 # AS8100
+97.75.145.12:8333 # AS22709
+102.132.192.141:8333 # AS37680
+103.14.245.250:8333 # AS24482
+103.85.38.205:8333 # AS134090
+103.88.92.78:8332 # AS17547
+103.99.168.100:8333 # AS6939
+103.99.168.140:8333 # AS6939
+103.99.170.210:8333 # AS54415
+103.99.170.220:8333 # AS54415
+103.100.44.70:8333 # AS10143
+103.178.236.27:8333 # AS49981
+103.209.12.144:8333 # AS58511
+104.59.147.15:8333 # AS7018
+104.129.171.121:8333 # AS174
+104.200.65.234:8333 # AS23033
+104.238.220.199:8333 # AS23470
+104.244.73.6:8333 # AS53667
+106.71.119.230:8333 # AS4804
+107.173.166.43:8333 # AS23352
+108.161.22.78:8333 # AS54154
+108.174.63.234:8333 # AS36352
+109.99.63.159:8333 # AS9050
+109.105.40.247:8333 # AS12570
+109.107.185.130:8333 # AS48282
+109.110.239.4:8333 # AS35432
+109.173.41.43:8333 # AS42610
+109.236.90.117:8333 # AS49981
+109.248.206.13:8333 # AS203493
+109.255.106.206:8333 # AS6830
+111.90.140.23:8333 # AS45839
+111.90.140.46:8333 # AS45839
+111.90.159.246:8333 # AS34309
+112.118.188.50:8333 # AS4760
+115.47.141.250:8885 # AS4134
+116.58.171.67:8333 # AS2514
+118.92.107.108:8333 # AS9500
+119.42.55.203:8333 # AS133159
+120.79.71.72:8333 # AS37963
+121.99.240.87:8333 # AS9790
+123.60.213.192:8333 # AS55990
+124.156.158.100:8333 # AS132203
+124.222.123.238:8333 # AS45090
+125.178.6.116:8333 # AS3786
+128.0.190.26:8333 # AS30764
+128.65.194.136:8333 # AS29222
+129.13.189.212:8333 # AS34878
+129.126.172.115:8333 # AS17547
+129.146.52.174:8333 # AS31898
+130.44.168.202:8333 # AS6079
+131.161.80.166:8333 # AS263694
+131.188.40.191:8333 # AS680
+134.195.185.52:8333 # AS13536
+135.134.238.47:8333 # AS4181
+135.180.218.58:8333 # AS46375
+135.181.215.237:8333 # AS24940
+136.29.109.180:8333 # AS19165
+136.32.238.6:8333 # AS16591
+136.56.170.96:8333 # AS16591
+137.25.38.108:8333 # AS20115
+137.226.34.46:8333 # AS680
+138.207.211.106:8333 # AS11776
+139.130.41.82:8333 # AS1221
+139.153.255.107:8333 # AS786
+140.190.12.129:8333 # AS14828
+142.54.181.218:8333 # AS32097
+143.177.229.149:8333 # AS50266
+143.178.64.10:8333 # AS50266
+144.24.245.183:8333 # AS31898
+144.126.130.178:8333 # AS40021
+146.4.124.129:8333 # AS3303
+146.71.69.103:8333 # AS7782
+146.83.56.69:8333 # AS23140
+147.194.177.165:8333 # AS15128
+149.90.214.78:8333 # AS12353
+149.102.157.156:8333 # AS13768
+151.248.156.55:8333 # AS8821
+151.252.193.245:8333 # AS29582
+153.92.93.114:8333 # AS41998
+154.211.6.2:8333 # AS140224
+156.17.103.2:8088 # AS8970
+156.146.177.221:8333 # AS1448
+157.131.143.173:8333 # AS46375
+158.58.188.37:8333 # AS57497
+158.248.39.239:8333 # AS29695
+159.89.230.128:8333 # AS14061
+159.196.3.239:8333 # AS4764
+159.224.189.250:8333 # AS13188
+160.72.51.154:8333 # AS46887
+161.29.236.55:8333 # AS4826
+161.97.119.166:8333 # AS51167
+161.246.11.230:8333 # AS9486
+162.62.18.226:8333 # AS132203
+162.250.123.179:8333 # AS19318
+162.250.191.222:8333 # AS26832
+162.254.118.20:8333 # AS6130
+163.172.81.70:8333 # AS12876
+164.90.47.8:8333 # AS53449
+165.228.174.117:8333 # AS1221
+166.70.145.151:8333 # AS6315
+168.91.238.8:8333 # AS11039
+170.253.11.25:8333 # AS15704
+171.103.170.115:8333 # AS7470
+172.93.166.135:8333 # AS22653
+172.103.217.236:8333 # AS25668
+172.105.21.216:8333 # AS63949
+172.112.153.95:8333 # AS20001
+173.3.218.91:8333 # AS6128
+173.12.119.133:8333 # AS7922
+173.34.127.181:8333 # AS812
+173.76.123.173:8333 # AS701
+173.176.198.68:8333 # AS5769
+173.208.152.218:8333 # AS32097
+173.241.227.243:8333 # AS19009
+173.246.27.7:8333 # AS1403
+173.255.240.205:8333 # AS63949
+174.30.47.15:8333 # AS209
+174.114.250.86:8333 # AS812
+174.138.35.229:8333 # AS14061
+174.142.191.136:8333 # AS32613
+176.10.143.190:8333 # AS8473
+176.74.136.237:8333 # AS35613
+176.118.220.29:8333 # AS60042
+176.126.116.7:8333 # AS20473
+176.126.167.10:8333 # AS8449
+176.212.185.153:8333 # AS9049
+176.235.209.186:8333 # AS34984
+177.81.236.117:8333 # AS28573
+177.89.205.70:8333 # AS28220
+178.48.168.12:8333 # AS21334
+178.124.162.209:8333 # AS6697
+178.159.98.133:8333 # AS202390
+178.196.89.209:8333 # AS3303
+178.236.137.63:8333 # AS44843
+178.252.123.24:8333 # AS42893
+179.43.170.186:8333 # AS51852
+180.150.46.187:8333 # AS4764
+181.117.128.140:8333 # AS19037
+184.19.19.16:8333 # AS5650
+185.21.217.48:8333 # AS200052
+185.25.48.184:8333 # AS61272
+185.31.136.246:8333 # AS47605
+185.52.93.45:8333 # AS39449
+185.64.116.15:8333 # AS31736
+185.68.249.91:8333 # AS51184
+185.98.54.20:8333 # AS39572
+185.107.83.55:8333 # AS43350
+185.140.253.169:8333 # AS200735
+185.148.145.74:8333 # AS44901
+185.165.170.19:8333 # AS3223
+185.167.113.59:8333 # AS207054
+185.185.26.141:8111 # AS201206
+185.197.163.136:8333 # AS60144
+185.209.12.76:8333 # AS212323
+185.209.70.17:8333 # AS204568
+185.227.156.226:8333 # AS209846
+185.233.189.210:8333 # AS61303
+185.239.221.5:8333 # AS61282
+185.244.100.106:8333 # AS2586
+185.254.97.164:8333 # AS44486
+186.33.167.11:8333 # AS1299
+186.176.98.37:8333 # AS262197
+186.249.217.25:8333 # AS7195
+186.250.95.132:8333 # AS262967
+188.32.14.31:8334 # AS42610
+188.35.167.14:8333 # AS34123
+188.68.45.143:8333 # AS47147
+188.117.200.212:8333 # AS25447
+188.138.88.14:8333 # AS20773
+188.151.237.158:8333 # AS1257
+188.154.236.49:8333 # AS6730
+189.123.177.128:8333 # AS4230
+190.123.27.11:8333 # AS52468
+190.145.127.254:8333 # AS14080
+192.69.53.77:8333 # AS11142
+192.146.137.44:8333 # AS25376
+192.222.24.54:8333 # AS22646
+192.222.147.141:8333 # AS1403
+193.32.127.162:60969 # AS39351
+193.111.198.187:8111 # AS24961
+193.196.37.62:8333 # AS34878
+194.13.80.185:15430 # AS47147
+194.147.113.201:8333 # AS21232
+194.165.30.20:8333 # AS35162
+194.191.239.98:8333 # AS1836
+195.56.63.4:8333 # AS5483
+195.56.63.10:8333 # AS5483
+195.123.239.185:8333 # AS64010
+195.140.226.154:8333 # AS35614
+198.1.231.6:8333 # AS30236
+198.148.112.27:8333 # AS35916
+199.126.234.237:8333 # AS395570
+199.193.174.173:8333 # AS7992
+199.247.7.208:8333 # AS20473
+200.122.181.46:8333 # AS3790
+201.191.6.103:8333 # AS11830
+201.212.36.209:8333 # AS7303
+201.221.234.200:8333 # AS27928
+202.108.211.135:8333 # AS4837
+202.169.17.178:8333 # AS137549
+202.177.24.140:8333 # AS7479
+203.130.48.117:8885 # AS54994
+203.132.94.196:8333 # AS38195
+205.178.41.124:8333 # AS11039
+206.72.201.228:8333 # AS19318
+206.192.203.0:8333 # AS7029
+206.223.153.52:8333 # AS19214
+207.134.216.145:8334 # AS395570
+207.188.154.50:8333 # AS15704
+207.229.46.80:8333 # AS852
+207.255.193.47:8333 # AS11776
+208.104.92.74:8333 # AS14615
+209.58.145.157:8333 # AS394380
+209.58.158.232:8335 # AS394380
+209.141.43.243:8333 # AS53667
+209.226.142.62:8333 # AS577
+209.237.127.227:8333 # AS1299
+209.237.133.54:8333 # AS53859
+211.248.90.50:8333 # AS4766
+212.21.18.78:8333 # AS20485
+212.34.225.118:8333 # AS44395
+212.51.146.137:8333 # AS13030
+212.227.211.87:8333 # AS8560
+213.0.69.76:8333 # AS3352
+213.5.36.58:8333 # AS49974
+213.47.64.105:8333 # AS8412
+213.89.135.151:8333 # AS1257
+213.141.154.201:8333 # AS12714
+213.159.198.45:8333 # AS8359
+213.184.244.24:8333 # AS60280
+213.214.66.182:8333 # AS43205
+213.226.123.76:8333 # AS49943
+216.146.251.8:8333 # AS54579
+216.186.238.14:8333 # AS12083
+217.5.150.114:8333 # AS3320
+217.15.178.11:8333 # AS25534
+217.24.239.109:8333 # AS9063
+217.64.47.138:8333 # AS39324
+217.73.80.104:8333 # AS44291
+217.79.181.38:8333 # AS24961
+217.92.55.246:8333 # AS3320
+217.113.121.169:8333 # AS8416
+217.115.116.250:8333 # AS30900
+217.155.244.170:8333 # AS13037
+217.170.124.170:8333 # AS35401
+220.132.135.54:8333 # AS3462
+220.233.178.199:8333 # AS38195
+222.154.111.46:8333 # AS4648
+[2001:1620:510::2]:8333 # AS13030
+[2001:19f0:6001:39aa:5400:3ff:fef0:916]:8333 # AS20473
+[2001:19f0:8001:f71:5400:4ff:fe10:6a63]:8333 # AS20473
+[2001:1bc0:c1::2000]:8333 # AS29686
+[2001:1c02:11e:3500:df25:6321:8260:d9be]:8333 # AS6830
+[2001:41d0:1004:1b79::]:8339 # AS16276
+[2001:41d0:203:3739::]:8333 # AS16276
+[2001:41d0:203:aacc::]:8333 # AS16276
+[2001:41d0:203:bb0a::]:8333 # AS16276
+[2001:41d0:2:bf8f::]:8333 # AS16276
+[2001:41d0:303:6586::]:8333 # AS16276
+[2001:41d0:602:4493::]:8333 # AS16276
+[2001:41d0:8:b9d8::1]:8333 # AS16276
+[2001:41d0:a:69a2::1]:8333 # AS16276
+[2001:41f0::62:6974:636f:696e]:8333 # AS6830
+[2001:44b8:256:5d11:216:3eff:fe39:d5d4]:8333 # AS4739
+[2001:470:1b62::]:8333 # AS6939
+[2001:470:1f07:803:20c:29ff:fe2d:5879]:8333 # AS6939
+[2001:470:1f15:106:e2d5:5eff:fe42:7ae5]:8333 # AS6939
+[2001:470:1f15:c43::11]:8333 # AS6939
+[2001:470:26:472::b7c]:8333 # AS6939
+[2001:470:75e9:1::10]:8333 # AS6939
+[2001:470:de5a::ec]:9333 # AS6939
+[2001:4ba0:babe:584::1]:8333 # AS24961
+[2001:4ba0:ffff:24::1]:8333 # AS24961
+[2001:4dd0:3564:0:30b7:1d7b:6fec:4c5c]:8333 # AS8422
+[2001:4dd0:3564:0:88e:b4ff:2ad0:699b]:8333 # AS8422
+[2001:4dd0:3564:0:9c1c:cc31:9fe8:5505]:8333 # AS8422
+[2001:4dd0:3564:0:a0c4:d41f:4c4:1bb0]:8333 # AS8422
+[2001:4dd0:3564:0:fd76:c1d3:1854:5bd9]:8333 # AS8422
+[2001:4dd0:3564:1::7676:8090]:8333 # AS8422
+[2001:4dd0:3564:1:b977:bd71:4612:8e40]:8333 # AS8422
+[2001:4dd0:af0e:3564::69:1]:8333 # AS8422
+[2001:4dd0:af0e:3564::69:90]:8333 # AS8422
+[2001:4de8:b1b2:1:0:dead:beef:7]:8333 # AS29208
+[2001:638:a000:4140::ffff:191]:8333 # AS680
+[2001:678:acc:42::]:8333 # AS60404
+[2001:67c:26b4:ff00::44]:8333 # AS25376
+[2001:67c:2db8:6::36]:8333 # AS39798
+[2001:7c0:2310:0:f816:3eff:fe0d:4ab6]:8333 # AS34878
+[2001:7c0:2310:0:f816:3eff:fe6c:4f58]:8333 # AS34878
+[2001:861:3246:a10::40]:8333 # AS5410
+[2001:b07:2e6:38d7:ba27:ebff:fe60:3dc1]:8333 # AS12874
+[2001:b07:6461:7811:489:d2da:e07:1af7]:8333 # AS12874
+[2001:b07:ac9:442b:79d6:bbbe:b37c:a783]:8333 # AS12874
+[2001:bc8:1600:0:208:a2ff:fe0c:8a2e]:8333 # AS12876
+[2001:bc8:323c:ff:a634:384f:1849:f4bc]:8333 # AS12876
+[2001:bc8:323c:ff:d217:c2ff:fe07:2cd9]:8333 # AS12876
+[2001:bc8:3bec:100::1]:8333 # AS12876
+[2002:2f5b:a5f9::2f5b:a5f9]:8885 # AS6939
+[2003:cb:8713:6102:aaa1:59ff:fe57:7779]:8333 # AS3320
+[2003:e0:370e:1400::5]:8333 # AS3320
+[2003:f6:3f10:6700:4c9f:7620:8324:d4a7]:8333 # AS3320
+[2400:2410:cea2:d00:41bc:c9ea:861b:51ee]:8333 # AS17676
+[2400:2411:a3e1:4900:2568:684b:e99:7120]:8333 # AS17676
+[2400:2411:a3e1:4900:2987:b88f:61e0:84fa]:8333 # AS17676
+[2400:3b00:20:c:bacb:29ff:feab:8886]:8333 # AS18229
+[2401:b140:1::100:210]:8333 # AS54415
+[2401:b140:1::100:220]:8333 # AS54415
+[2401:b140::42:100]:8333 # AS6939
+[2401:b140::44:130]:8333 # AS6939
+[2401:d002:3902:700:d72c:5e22:4e95:389d]:8333 # AS38195
+[2404:4408:6752:c000::1999]:8333 # AS9790
+[2404:7a85:4161:2b00:49a1:427a:fac:3409]:8333 # AS2518
+[2405:9800:b972:ab58:c05:e938:267e:271]:8333 # AS45430
+[2406:da11:169:b03:32b5:f901:9f7c:3e4b]:8333 # AS16509
+[2406:da14:335:b601:ceb7:b4fc:a855:f3a5]:8333 # AS16509
+[2406:da1e:a4e:8a03:2aad:496b:768d:e497]:8333 # AS16509
+[2407:8800:bc61:2202:a0c6:107:502b:4e3b]:8333 # AS7545
+[2409:10:ca20:1df0:224:e8ff:fe1f:60d9]:8333 # AS55391
+[2600:1700:22f1:641f:e8:39c8:eb1d:a1eb]:8333 # AS7018
+[2600:1700:9c5d:ed0::38]:8333 # AS7018
+[2600:1700:9c5d:ed0:d0d6:1d9:5cc2:ab47]:8333 # AS7018
+[2600:1702:1ce0:4010::40]:8333 # AS7018
+[2600:1f14:40e:e301:d155:aa3a:77be:960e]:8333 # AS16509
+[2600:1f16:a08:b901:1afa:ef4e:4ce7:2ba4]:8333 # AS16509
+[2600:1f1c:2d3:2403:5bac:3fc6:6513:7a63]:8333 # AS16509
+[2600:2104:1003:c5ab:dc5e:90ff:fe18:1d08]:8333 # AS11404
+[2600:3c00::f03c:92ff:fe92:2745]:8333 # AS63949
+[2600:3c00::f03c:92ff:fecf:61b6]:8333 # AS63949
+[2600:3c00::f03c:93ff:feb3:1b6]:8333 # AS63949
+[2600:3c00:e002:2e32::1:14]:8333 # AS63949
+[2600:3c02::f03c:92ff:fe5d:9fb]:8333 # AS63949
+[2600:4040:2854:5e00:c6e9:84ff:fe46:ee8]:8666 # AS13786
+[2600:6c54:7100:1ad1:bddf:550e:91be:f9e1]:8333 # AS20115
+[2600:8805:2400:14e:12dd:b1ff:fef2:3013]:8333 # AS22773
+[2601:184:300:bde:3c29:8e94:1ba8:fde3]:8333 # AS7922
+[2601:18c:8080:300f:219:d1ff:fe75:dc2f]:8333 # AS7922
+[2601:18d:4600:43f1:20e7:b3ff:fecf:a99]:8333 # AS7922
+[2601:18d:8701:c290::3330]:8333 # AS7922
+[2601:246:4d7f:9e28:f321:36ca:7a71:c687]:8333 # AS7922
+[2601:640:c201:960d:86eb:f27d:66a2:f2c1]:8333 # AS7922
+[2602:241:75d1:2b90::7840]:8333 # AS46375
+[2602:ffb8::208:72:57:200]:8333 # AS2914
+[2603:3004:6a1:3800:851f:584d:7aba:affb]:8333 # AS7922
+[2603:3004:6a1:3800::4402]:8333 # AS7922
+[2603:3004:70d:1400:8532:2900:ce6f:acdf]:8333 # AS7922
+[2603:3004:745:900:f0d7:556a:a8c:ced5]:8333 # AS7922
+[2603:6080:c000:5d8a::104f]:8333 # AS7843
+[2603:8000:d100:8991:cc29:ccff:fe42:300c]:8333 # AS7843
+[2603:8080:1f07:6fdd:7de2:d969:78c9:b7ea]:8333 # AS7843
+[2603:8080:7300:531::13ea]:8333 # AS7843
+[2603:80a0:703:40f8::38]:8333 # AS7843
+[2604:180:f3::218]:8333 # AS3842
+[2604:3d08:0:5:d941:4b03:a093:131b]:8333 # AS6327
+[2604:7c00:120:4b::eb24]:8333 # AS174
+[2604:a00:21:3043:bf6a:535e:dfeb:5b7b]:8333 # AS19318
+[2604:a880:400:d0::1ce7:4001]:8333 # AS14061
+[2604:a880:400:d0::1d44:e001]:8333 # AS14061
+[2604:a880:400:d0::261f:6001]:8333 # AS14061
+[2604:a880:400:d1::7e2:e001]:8333 # AS14061
+[2604:a880:4:1d0::14:3000]:8333 # AS14061
+[2604:a880:4:1d0::e5:b000]:8333 # AS14061
+[2605:6400:30:f220::]:8333 # AS53667
+[2605:6f80:0:7:fc1b:ccff:fe8a:d822]:8333 # AS53340
+[2605:a140:2076:8253::1]:8333 # AS40021
+[2605:a140:3007:1287::1]:8333 # AS40021
+[2605:ae00:203::203]:8333 # AS7819
+[2605:c000:2a0a:1::102]:8333 # AS7393
+[2607:1a00:1:d::11:7c4d]:8333 # AS22653
+[2607:5300:203:1214::]:8333 # AS16276
+[2607:9280:b:73b:250:56ff:fe14:25b5]:8333 # AS395502
+[2607:9280:b:73b:250:56ff:fe21:9c2f]:8333 # AS395502
+[2607:9280:b:73b:250:56ff:fe21:bf32]:8333 # AS395502
+[2607:9280:b:73b:250:56ff:fe33:4d1b]:8333 # AS395502
+[2607:9280:b:73b:250:56ff:fe3d:401]:8333 # AS395502
+[2607:f2c0:e1c2:69:12c3:7bff:fe4d:9431]:8333 # AS5645
+[2607:f2c0:e1c2:69:ecb2:6e88:9f33:5057]:8333 # AS5645
+[2620:6:2003:105:2d8:61ff:fe0f:853]:8333 # AS25682
+[2620:6e:a000:1:42:42:42:42]:8333 # AS397444
+[2620:a6:2000:1::3:d570]:8333 # AS27566
+[2620:a6:2000:1::5:162a]:8333 # AS27566
+[2620:a6:2000:1::5:1631]:8333 # AS27566
+[2620:a6:2000:1::c:e634]:8333 # AS27566
+[2800:40:33:8ab:a0e7:b215:fc83:5c31]:8333 # AS16814
+[2800:bf0:149:f4b:f8df:8d7d:801b:e25e]:8333 # AS27947
+[2804:14c:198:80d5:7603:41d1:d3fc:e797]:8333 # AS28573
+[2804:14d:ae81:827b:99a8:1e3f:6db2:29db]:8333 # AS4230
+[2804:d57:5537:4800:3e7c:3fff:fe7b:80aa]:8333 # AS8167
+[2a00:12e0:101:99:20c:29ff:fe29:d03f]:8333 # AS6798
+[2a00:1328:e101:c00::163]:8333 # AS31078
+[2a00:1398:4:2a03:215:5dff:fed6:1033]:8333 # AS34878
+[2a00:1398:4:2a03::bc03]:8333 # AS34878
+[2a00:1630:10:1003:0:b19:b00b:babe]:8333 # AS49544
+[2a00:1768:2001:27::ef6a]:8333 # AS43350
+[2a00:1828:a004:2::666]:8333 # AS34240
+[2a00:1c10:2:709::217]:22220 # AS50300
+[2a00:1f40:5001:108:5d17:7703:b0f5:4133]:8333 # AS42864
+[2a00:23c5:fe80:7301:d6ae:52ff:fed5:56a5]:8333 # AS2856
+[2a00:23c6:5c91:5808:c05a:4dff:fe65:9d69]:8333 # AS2856
+[2a00:6020:1bfa:d400:20c:29ff:fe61:4a4c]:8333 # AS60294
+[2a00:6020:b482:9200:491a:358c:d8f7:1da]:8333 # AS60294
+[2a00:6020:b489:2000:5054:ff:fefc:5ed8]:8333 # AS60294
+[2a00:7c80:0:25::e37a]:8333 # AS49981
+[2a00:7c80:0:71::8]:8333 # AS49981
+[2a00:8a60:e012:a00::21]:8333 # AS680
+[2a00:ae40:240e:3200::3]:8333 # AS50923
+[2a00:bbe0:cc:0:62a4:4cff:fe23:7510]:8333 # AS47605
+[2a00:ca8:a1f:3025:f949:e442:c940:13e8]:8333 # AS30764
+[2a00:d4e0:2:d002:4467:31e0:6fa5:b3ef]:8333 # AS15600
+[2a00:ee2:1200:1900:8d3:d2ff:feb1:bc58]:8333 # AS5603
+[2a01:238:420f:9200:fa5a:1a4b:1e6a:fadf]:8333 # AS6724
+[2a01:238:4389:c400:3b26:d94e:38d5:44ef]:8333 # AS6724
+[2a01:490:16:301::2]:8333 # AS8251
+[2a01:4b00:807c:3100:cda1:c6a:2bad:2418]:8333 # AS56478
+[2a01:4f8:141:2254::2]:8333 # AS24940
+[2a01:4f8:173:230a::2]:8333 # AS24940
+[2a01:4f8:190:91c4::2]:8333 # AS24940
+[2a01:4f8:200:7222::2]:8333 # AS24940
+[2a01:4f8:202:3e6::2]:8333 # AS24940
+[2a01:4f8:221:44d7::2]:8333 # AS24940
+[2a01:4f8:231:915::2]:8333 # AS24940
+[2a01:4f9:2a:1ce0::2]:8333 # AS24940
+[2a01:4f9:2b:29a::2]:8333 # AS24940
+[2a01:4f9:4a:31de::2]:8333 # AS24940
+[2a01:5200:6c:6162:7a61:746b:6f2e:736b]:8333 # AS6855
+[2a01:6380:fffe:73:10fb:d012:8581:b4d7]:8333 # AS25540
+[2a01:7a7:2:2804:ae1f:6bff:fe9d:6c94]:8333 # AS20773
+[2a01:7c8:aaac:89:5054:ff:feb7:f5cb]:8333 # AS20857
+[2a01:7c8:aac9:c9:5054:ff:fedf:ff95]:8333 # AS20857
+[2a01:7c8:d001:1c1:5054:ff:feee:3e1a]:8333 # AS20857
+[2a01:7c8:d009:2aa:5054:ff:fe1b:a196]:11520 # AS20857
+[2a01:7c8:fffa:50e:ddfe:c924:ca0a:cbab]:8333 # AS20857
+[2a01:7e00::f03c:93ff:fe59:66dc]:8333 # AS63949
+[2a01:7e01::f03c:93ff:fe3b:bb5b]:8333 # AS63949
+[2a01:8740:1:ffc5::8c6a]:8333 # AS57344
+[2a01:9f40:a000::100]:8333 # AS42908
+[2a01:cb00:d3d:7700:227:eff:fe28:c565]:8333 # AS3215
+[2a01:e0a:20:7350:919c:b1c3:8b83:adf9]:8333 # AS12322
+[2a01:e0a:301:7010:b87d:e14b:cea9:b998]:8333 # AS12322
+[2a01:e0a:48b:2d10:94f2:4d5c:ca5f:bf49]:8333 # AS12322
+[2a01:e0a:530:a0a0:f465:af5:be1b:9075]:8333 # AS12322
+[2a01:e0a:aa7:c8c0:9679:affa:b6e5:efc7]:8333 # AS12322
+[2a01:e11:100c:70:cbc8:9e31:4b77:1626]:8333 # AS12322
+[2a01:e34:ee78:3060:230:48ff:fe81:f1c6]:8333 # AS12322
+[2a02:1210:14a9:6700:a00:27ff:fe4e:82b6]:8333 # AS3303
+[2a02:1210:4639:f00:10a7:e965:509a:7a4a]:8333 # AS3303
+[2a02:1210:7c92:5100:211:32ff:feae:152d]:8333 # AS3303
+[2a02:1210:86bf:f100:3178:d700:d44d:6bb1]:8333 # AS3303
+[2a02:1210:9487:a200:edc1:93a4:945:9a92]:8333 # AS3303
+[2a02:168:420b:a::20]:8333 # AS13030
+[2a02:168:6328:0:4a21:bff:fe26:38c3]:8333 # AS13030
+[2a02:168:676e:0:e65f:1ff:fe09:3591]:8333 # AS13030
+[2a02:1748:f39f:5872:dead:beef:b1ac:c0fe]:8333 # AS51184
+[2a02:180:1:1::517:10b6]:8333 # AS35366
+[2a02:2168:a379:d100:96de:80ff:fea3:fd00]:8333 # AS42610
+[2a02:2780:9000:70::7]:8333 # AS35434
+[2a02:2780:9000:70::f]:8333 # AS35434
+[2a02:2780::e01a]:8333 # AS35434
+[2a02:2e02:3900:5400:a099:e1ff:feb6:d0e]:8333 # AS12479
+[2a02:2f05:660e:8b00::1]:8333 # AS48571
+[2a02:58:97:7d20::60]:8333 # AS25596
+[2a02:6d40:3073:c01:dea6:32ff:fe44:4b25]:8333 # AS42652
+[2a02:7a01::91:228:45:130]:8333 # AS16019
+[2a02:7b40:5928:89::1]:8333 # AS62282
+[2a02:7b40:c3b5:f583::1]:8333 # AS62282
+[2a02:8308:8087:aa00:9ea8:1b2:ef98:56bf]:8333 # AS16019
+[2a02:842a:1df:8a01:1e1b:dff:fe0b:236d]:8333 # AS15557
+[2a02:a44d:14d6:1:2c0:8ff:fe8f:b3b2]:8333 # AS1136
+[2a02:a45a:94cd:f00d::1]:8333 # AS1136
+[2a02:a45f:3b9d:30::3]:8333 # AS1136
+[2a02:a467:7833:1:7285:c2ff:fe2c:21e9]:8333 # AS1136
+[2a02:aa14:2380:b300:4040:be88:8b01:d38]:8333 # AS6830
+[2a02:c206:2044:9826::1]:8333 # AS51167
+[2a02:c206:2082:1246::1]:8333 # AS51167
+[2a02:c206:3008:2368::1]:8333 # AS51167
+[2a02:c207:0:4971::1]:5332 # AS51167
+[2a02:c207:2014:4199::1]:8333 # AS51167
+[2a02:c207:2024:6115::1]:8333 # AS51167
+[2a02:c207:2026:6682::1]:8333 # AS51167
+[2a02:c207:3002:7468::1]:8333 # AS51167
+[2a02:e98:20:1504::1]:8333 # AS24641
+[2a03:4000:6:416c::43]:8333 # AS47147
+[2a03:4000:6:f814:548b:17ff:fe31:b64a]:8333 # AS47147
+[2a03:6000:870:0:46:23:87:218]:8333 # AS51088
+[2a03:94e0:ffff:185:243:218:0:19]:8333 # AS56655
+[2a03:b0c0:1:e0::397:6001]:8333 # AS14061
+[2a03:b0c0:2:f0::163:3001]:8333 # AS14061
+[2a03:b0c0:2:f0::18a:d001]:8333 # AS14061
+[2a03:b0c0:3:d0::f3e:2001]:8333 # AS14061
+[2a03:e2c0:1347::2]:8333 # AS50113
+[2a03:ec0:0:928::701:701]:8333 # AS199669
+[2a04:52c0:103:c455::1]:8334 # AS60404
+[2a04:52c0:3007:200::2000]:8333 # AS60404
+[2a04:bc40:1dc3:8d::2:1001]:8333 # AS35277
+[2a05:1500:702:0:1c00:40ff:fe00:c]:8333 # AS48635
+[2a05:3580:d101:3700::]:8333 # AS20764
+[2a05:3580:db0b:1600:c489:76ed:313d:b33]:8333 # AS20764
+[2a05:d014:a55:4001:8127:afa7:daf9:d91b]:8333 # AS16509
+[2a05:d014:a55:4001:f6ab:dd5e:4039:b46c]:8333 # AS16509
+[2a05:d014:a55:4003:6523:50a1:152:e88c]:8333 # AS16509
+[2a05:d01a:b7b:3c01:8bf7:ae14:afb3:33ae]:8333 # AS16509
+[2a05:f480:1800:697:5400:2ff:feb6:c36d]:8333 # AS20473
+[2a06:e040:7603:2918:c6ef:464e:9fe5:73ec]:8333 # AS198507
+[2a07:abc4::1:946]:8333 # AS62000
+[2a09:2681:102::210]:8333 # AS61282
+[2a0a:c801:1:7::183]:8333 # AS39798
+[2a0c:5a80:1210:a800:6af7:28ff:fee5:6b3a]:8333 # AS57269
+[2a0d:5600:24:a8e::a91e]:55373 # AS9009
+[2a0d:7c40:3000:b04::2]:8333 # AS54290
+[2a0d:8340:24::2]:8333 # AS50113
+[2a0f:df00:0:2010::162]:8333 # AS41281
+[2a10:3781:16b9:1:fe3f:dbff:fe04:2d4c]:8333 # AS206238
+[2a10:3781:84b:1:b123:6306:943a:f09b]:8333 # AS206238
+[2a10:d200:1:33:a6bf:1ff:fe6a:46a9]:8333 # AS212323
+[2c0f:f4c0:2202:20b0:261c:4ff:fe14:daa0]:8333 # AS327693
+[2c0f:f8f0:da51:0:70c3:eea9:9717:9579]:8333 # AS30844
-# manually added 2021-03 for minimal torv3 bootstrap support
-2g5qfdkn2vvcbqhzcyvyiitg4ceukybxklraxjnu7atlhd22gdwywaid.onion:8333
-2jmtxvyup3ijr7u6uvu7ijtnojx4g5wodvaedivbv74w4vzntxbrhvad.onion:8333
-37m62wn7dz3uqpathpc4qfmgrbupachj52nt3jbtbjugpbu54kbud7yd.onion:8333
+# manually updated 2022-08 for minimal torv3 bootstrap support
5g72ppm3krkorsfopcm2bi7wlv4ohhs4u4mlseymasn7g7zhdcyjpfid.onion:8333
-7cgwjuwi5ehvcay4tazy7ya6463bndjk6xzrttw5t3xbpq4p22q6fyid.onion:8333
-7pyrpvqdhmayxggpcyqn5l3m5vqkw3qubnmgwlpya2mdo6x7pih7r7id.onion:8333
b64xcbleqmwgq2u46bh4hegnlrzzvxntyzbmucn3zt7cssm7y4ubv3id.onion:8333
-ejxefzf5fpst4mg2rib7grksvscl7p6fvjp6agzgfc2yglxnjtxc3aid.onion:8333
fjdyxicpm4o42xmedlwl3uvk5gmqdfs5j37wir52327vncjzvtpfv7yd.onion:8333
fpz6r5ppsakkwypjcglz6gcnwt7ytfhxskkfhzu62tnylcknh3eq6pad.onion:8333
-fzhn4uoxfbfss7h7d6ffbn266ca432ekbbzvqtsdd55ylgxn4jucm5qd.onion:8333
gxo5anvfnffnftfy5frkgvplq3rpga2ie3tcblo2vl754fvnhgorn5yd.onion:8333
ifdu5qvbofrt4ekui2iyb3kbcyzcsglazhx2hn4wfskkrx2v24qxriid.onion:8333
itz3oxsihs62muvknc237xabl5f6w6rfznfhbpayrslv2j2ubels47yd.onion:8333
-lrjh6fywjqttmlifuemq3puhvmshxzzyhoqx7uoufali57eypuenzzid.onion:8333
+kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion:8333
m7cbpjolo662uel7rpaid46as2otcj44vvwg3gccodnvaeuwbm3anbyd.onion:8333
-opnyfyeiibe5qo5a3wbxzbb4xdiagc32bbce46owmertdknta5mi7uyd.onion:8333
-owjsdxmzla6d7lrwkbmetywqym5cyswpihciesfl5qdv2vrmwsgy4uqd.onion:8333
-q7kgmd7n7h27ds4fg7wocgniuqb3oe2zxp4nfe4skd5da6wyipibqzqd.onion:8333
+mwmfluek4au6mxxpw6fy7sjhkm65bdfc7izc7lpz3trewfdghyrzsbid.onion:8333
rp7k2go3s5lyj3fnj6zn62ktarlrsft2ohlsxkyd7v3e3idqyptvread.onion:8333
-sys54sv4xv3hn3sdiv3oadmzqpgyhd4u4xphv4xqk64ckvaxzm57a7yd.onion:8333
-tddeij4qigtjr6jfnrmq6btnirmq5msgwcsdpcdjr7atftm7cxlqztid.onion:8333
-vi5bnbxkleeqi6hfccjochnn65lcxlfqs4uwgmhudph554zibiusqnad.onion:8333
-xqt25cobm5zqucac3634zfght72he6u3eagfyej5ellbhcdgos7t2had.onion:8333
-# manually added 2021-08 for minimal i2p bootstrap support
+# manually updated 2022-08 for minimal i2p bootstrap support
+255fhcp6ajvftnyo7bwz3an3t4a4brhopm3bamyh2iu5r3gnr2rq.b32.i2p:0
+27yrtht5b5bzom2w5ajb27najuqvuydtzb7bavlak25wkufec5mq.b32.i2p:0
+2el6enckmfyiwbfcwsygkwksovtynzsigmyv3bzyk7j7qqahooua.b32.i2p:0
+3gocb7wc4zvbmmebktet7gujccuux4ifk3kqilnxnj5wpdpqx2hq.b32.i2p:0
+3tns2oov4tnllntotazy6umzkq4fhkco3iu5rnkxtu3pbfzxda7q.b32.i2p:0
+4fcc23wt3hyjk3csfzcdyjz5pcwg5dzhdqgma6bch2qyiakcbboa.b32.i2p:0
+4osyqeknhx5qf3a73jeimexwclmt42cju6xdp7icja4ixxguu2hq.b32.i2p:0
+4umsi4nlmgyp4rckosg4vegd2ysljvid47zu7pqsollkaszcbpqq.b32.i2p:0
+52v6uo6crlrlhzphslyiqblirux6olgsaa45ixih7sq5np4jujaa.b32.i2p:0
+6j2ezegd3e2e2x3o3pox335f5vxfthrrigkdrbgfbdjchm5h4awa.b32.i2p:0
+6n36ljyr55szci5ygidmxqer64qr24f4qmnymnbvgehz7qinxnla.b32.i2p:0
+72yjs6mvlby3ky6mgpvvlemmwq5pfcznrzd34jkhclgrishqdxva.b32.i2p:0
+7r4ri53lby2i3xqbgpw3idvhzeku7ubhftlf72ldqkg5kde6dauq.b32.i2p:0
a5qsnv3maw77mlmmzlcglu6twje6ttctd3fhpbfwcbpmewx6fczq.b32.i2p:0
-bitcornrd36coazsbzsz4pdebyzvaplmsalq4kpoljmn6cg6x5zq.b32.i2p:0
+aovep2pco7v2k4rheofrgytbgk23eg22dczpsjqgqtxcqqvmxk6a.b32.i2p:0
+bddbsmkas3z6fakorbkfjhv77i4hv6rysyjsvrdjukxolfghc23q.b32.i2p:0
+bitcoi656nll5hu6u7ddzrmzysdtwtnzcnrjd4rfdqbeey7dmn5a.b32.i2p:0
+brifkruhlkgrj65hffybrjrjqcgdgqs2r7siizb5b2232nruik3a.b32.i2p:0
c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p:0
-dhtq2p76tyhi442aidb3vd2bv7yxxjuddpb2jydnnrl2ons5bhha.b32.i2p:0
+day3hgxyrtwjslt54sikevbhxxs4qzo7d6vi72ipmscqtq3qmijq.b32.i2p:0
+di2zq6fr3fegf2jdcd7hdwyql4umr462gonsns2nxz5qg5vz4bka.b32.i2p:0
+e55k6wu46rzp4pg5pk5npgbr3zz45bc3ihtzu2xcye5vwnzdy7pq.b32.i2p:0
+eciohu5nq7vsvwjjc52epskuk75d24iccgzmhbzrwonw6lx4gdva.b32.i2p:0
+ejlnngarmhqvune74ko7kk55xtgbz5i5ncs4vmnvjpy3l7y63xaa.b32.i2p:0
+g47cqoppu26pr4n2cfaioqx7lbdi7mea7yqhlrkdz3wjwxjxdh2a.b32.i2p:0
h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p:0
-hnbbyjpxx54623l555sta7pocy3se4sdgmuebi5k6reesz5rjp6q.b32.i2p:0
+hhfi4yqkg2twqiwezrfksftjjofbyx3ojkmlnfmcwntgnrjjhkya.b32.i2p:0
+hpiibrflqkbrcshfhmrtwfyeb7mds7a3obzwrgarejevddzamvsq.b32.i2p:0
+i4pyhsfdq4247dunel7paatdaq5gusi2hnybp2yf5wxwdnrgxaqq.b32.i2p:0
+iw6tgpmbdykffceku5da6nzf2bmz66fvp5fpcvemfu3df6aq6pga.b32.i2p:0
+jkfuajo4ayvo2rbv5qdj443q6adqmnormbhsf2f7rlp5t24xomda.b32.i2p:0
jz3s4eurm5vzjresf4mwo7oni4bk36daolwxh4iqtewakylgkxmq.b32.i2p:0
-kokkmpquqlkptu5hkmzqlttsmtwxicldr4so7wqsufk6bwf32nma.b32.i2p:0
+liu75cvktv4icbctg72w7nxbk4eibt7wamizfdii4omz7gcke5vq.b32.i2p:0
+ljsquuu3y4xje6l32p32inn6r2y6ull6oocgup6jtjrohrqxbz6a.b32.i2p:0
+lrah7acdsgopybg43shadwwiv6igezaw64i6jb5muqdg7dmhj3la.b32.i2p:0
+lzuu6mjtu7vd55d2biphicihufipoa7vyym6xfnkmmlra3tiziia.b32.i2p:0
+m6bpynxkv2ktwxkg6p2gyudjfhdupb6kuzabeqdnckkdkf4kxjla.b32.i2p:0
+m6v454xd6p3bt5swujgmveklsp7lzbkqlqqfc2p36cjlwv5dbucq.b32.i2p:0
+mlgeizrroynuhpxbzeosajt5u4ddcvynxfmcbm6kwjpaufilxigq.b32.i2p:0
+ofubxr2ir7u2guzjwyrvujicivzmvinwa36nuzlrg7tnsmebal7a.b32.i2p:0
+okfxeoh6itu4f5f43dhbzvkqwfrvm5c66lj6lvjj4q2b35i4pk4q.b32.i2p:0
+oz2ia3flpm3du2tyusulrn7h7e2eo3juzkrmn34bvnrlcrugv7ia.b32.i2p:0
+qd6jlsevsexww3wefpqs7iglxb3f63y4e6ydulfzrvwflpicmdqa.b32.i2p:0
+qddg7myylinn4tw6kdjmmp6fsyetkosnrbp2gsjx77tmkqyqv6ua.b32.i2p:0
+rizfinyses2r3or4iubs5wx66gdy6mpf73w7uobfacm2l5cral3q.b32.i2p:0
+s5hhjtmlg53bko3nwwskas7xgsmeqzy6thtsj5aa64djyrljgqaq.b32.i2p:0
sedndhv5vpcgdmykyi5st4yqhdxl3hpdtglta4do435wupahhx6q.b32.i2p:0
+tsl4dlpu2id252b6crbdnblruct664se6f2iw35fuqwa3te7wcoq.b32.i2p:0
+tugq6wa2ls2bv27pr2iy3da3k5ow3fzefbcvjcr22uc7w5vmevja.b32.i2p:0
+usztavbib756k5vqggzgkyswoj6mttihjvp3c2pa642t2mb4pvsa.b32.i2p:0
+vgu6llqbyjphml25umd5ztvyxrxuplz2g74fzbx75g3kkaetoyiq.b32.i2p:0
+wjrul5jwwb4vqdmkkrjbmly7osj6amecdpsac5xvaoqrti4nb3ha.b32.i2p:0
+wvktcp7hy4l6immhi5cxyz2dlsbhhvtcmskjemrnqehacnoap23q.b32.i2p:0
wwbw7nqr3ahkqv62cuqfwgtneekvvpnuc4i4f6yo7tpoqjswvcwa.b32.i2p:0
+xlqndzjoe5nr2nsxo6xwibh44ghyz4jfqevu62xykvemextpmjbq.b32.i2p:0
+yc4xwin5ujenvcr6ynwkz7lnmmq3nmzxvfguele6ovqqpxgjvonq.b32.i2p:0
+zdoabsg7ugzothyawodjhq54nvlofa746rxfkxpnjzj6nukmha6a.b32.i2p:0
zsxwyo6qcn3chqzwxnseusqgsnuw3maqnztkiypyfxtya4snkoka.b32.i2p:0
+zysrlpii5ftrzivfcyhdrwpeyyqddbrdefnfu5q6otk5gtugmh2a.b32.i2p:0
# manually added 2022-01 for minimal cjdns bootstrap support
[fc32:17ea:e415:c3bf:9808:149d:b5a2:c9aa]:8333
diff --git a/contrib/seeds/nodes_main_manual.txt b/contrib/seeds/nodes_main_manual.txt
index a6e0b8763a..286448d95d 100644
--- a/contrib/seeds/nodes_main_manual.txt
+++ b/contrib/seeds/nodes_main_manual.txt
@@ -1,42 +1,77 @@
-# manually added 2021-03 for minimal torv3 bootstrap support
-2g5qfdkn2vvcbqhzcyvyiitg4ceukybxklraxjnu7atlhd22gdwywaid.onion:8333
-2jmtxvyup3ijr7u6uvu7ijtnojx4g5wodvaedivbv74w4vzntxbrhvad.onion:8333
-37m62wn7dz3uqpathpc4qfmgrbupachj52nt3jbtbjugpbu54kbud7yd.onion:8333
+# manually updated 2022-08 for minimal torv3 bootstrap support
5g72ppm3krkorsfopcm2bi7wlv4ohhs4u4mlseymasn7g7zhdcyjpfid.onion:8333
-7cgwjuwi5ehvcay4tazy7ya6463bndjk6xzrttw5t3xbpq4p22q6fyid.onion:8333
-7pyrpvqdhmayxggpcyqn5l3m5vqkw3qubnmgwlpya2mdo6x7pih7r7id.onion:8333
b64xcbleqmwgq2u46bh4hegnlrzzvxntyzbmucn3zt7cssm7y4ubv3id.onion:8333
-ejxefzf5fpst4mg2rib7grksvscl7p6fvjp6agzgfc2yglxnjtxc3aid.onion:8333
fjdyxicpm4o42xmedlwl3uvk5gmqdfs5j37wir52327vncjzvtpfv7yd.onion:8333
fpz6r5ppsakkwypjcglz6gcnwt7ytfhxskkfhzu62tnylcknh3eq6pad.onion:8333
-fzhn4uoxfbfss7h7d6ffbn266ca432ekbbzvqtsdd55ylgxn4jucm5qd.onion:8333
gxo5anvfnffnftfy5frkgvplq3rpga2ie3tcblo2vl754fvnhgorn5yd.onion:8333
ifdu5qvbofrt4ekui2iyb3kbcyzcsglazhx2hn4wfskkrx2v24qxriid.onion:8333
itz3oxsihs62muvknc237xabl5f6w6rfznfhbpayrslv2j2ubels47yd.onion:8333
-lrjh6fywjqttmlifuemq3puhvmshxzzyhoqx7uoufali57eypuenzzid.onion:8333
+kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion:8333
m7cbpjolo662uel7rpaid46as2otcj44vvwg3gccodnvaeuwbm3anbyd.onion:8333
-opnyfyeiibe5qo5a3wbxzbb4xdiagc32bbce46owmertdknta5mi7uyd.onion:8333
-owjsdxmzla6d7lrwkbmetywqym5cyswpihciesfl5qdv2vrmwsgy4uqd.onion:8333
-q7kgmd7n7h27ds4fg7wocgniuqb3oe2zxp4nfe4skd5da6wyipibqzqd.onion:8333
+mwmfluek4au6mxxpw6fy7sjhkm65bdfc7izc7lpz3trewfdghyrzsbid.onion:8333
rp7k2go3s5lyj3fnj6zn62ktarlrsft2ohlsxkyd7v3e3idqyptvread.onion:8333
-sys54sv4xv3hn3sdiv3oadmzqpgyhd4u4xphv4xqk64ckvaxzm57a7yd.onion:8333
-tddeij4qigtjr6jfnrmq6btnirmq5msgwcsdpcdjr7atftm7cxlqztid.onion:8333
-vi5bnbxkleeqi6hfccjochnn65lcxlfqs4uwgmhudph554zibiusqnad.onion:8333
-xqt25cobm5zqucac3634zfght72he6u3eagfyej5ellbhcdgos7t2had.onion:8333
-# manually added 2021-08 for minimal i2p bootstrap support
+# manually updated 2022-08 for minimal i2p bootstrap support
+255fhcp6ajvftnyo7bwz3an3t4a4brhopm3bamyh2iu5r3gnr2rq.b32.i2p:0
+27yrtht5b5bzom2w5ajb27najuqvuydtzb7bavlak25wkufec5mq.b32.i2p:0
+2el6enckmfyiwbfcwsygkwksovtynzsigmyv3bzyk7j7qqahooua.b32.i2p:0
+3gocb7wc4zvbmmebktet7gujccuux4ifk3kqilnxnj5wpdpqx2hq.b32.i2p:0
+3tns2oov4tnllntotazy6umzkq4fhkco3iu5rnkxtu3pbfzxda7q.b32.i2p:0
+4fcc23wt3hyjk3csfzcdyjz5pcwg5dzhdqgma6bch2qyiakcbboa.b32.i2p:0
+4osyqeknhx5qf3a73jeimexwclmt42cju6xdp7icja4ixxguu2hq.b32.i2p:0
+4umsi4nlmgyp4rckosg4vegd2ysljvid47zu7pqsollkaszcbpqq.b32.i2p:0
+52v6uo6crlrlhzphslyiqblirux6olgsaa45ixih7sq5np4jujaa.b32.i2p:0
+6j2ezegd3e2e2x3o3pox335f5vxfthrrigkdrbgfbdjchm5h4awa.b32.i2p:0
+6n36ljyr55szci5ygidmxqer64qr24f4qmnymnbvgehz7qinxnla.b32.i2p:0
+72yjs6mvlby3ky6mgpvvlemmwq5pfcznrzd34jkhclgrishqdxva.b32.i2p:0
+7r4ri53lby2i3xqbgpw3idvhzeku7ubhftlf72ldqkg5kde6dauq.b32.i2p:0
a5qsnv3maw77mlmmzlcglu6twje6ttctd3fhpbfwcbpmewx6fczq.b32.i2p:0
-bitcornrd36coazsbzsz4pdebyzvaplmsalq4kpoljmn6cg6x5zq.b32.i2p:0
+aovep2pco7v2k4rheofrgytbgk23eg22dczpsjqgqtxcqqvmxk6a.b32.i2p:0
+bddbsmkas3z6fakorbkfjhv77i4hv6rysyjsvrdjukxolfghc23q.b32.i2p:0
+bitcoi656nll5hu6u7ddzrmzysdtwtnzcnrjd4rfdqbeey7dmn5a.b32.i2p:0
+brifkruhlkgrj65hffybrjrjqcgdgqs2r7siizb5b2232nruik3a.b32.i2p:0
c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p:0
-dhtq2p76tyhi442aidb3vd2bv7yxxjuddpb2jydnnrl2ons5bhha.b32.i2p:0
+day3hgxyrtwjslt54sikevbhxxs4qzo7d6vi72ipmscqtq3qmijq.b32.i2p:0
+di2zq6fr3fegf2jdcd7hdwyql4umr462gonsns2nxz5qg5vz4bka.b32.i2p:0
+e55k6wu46rzp4pg5pk5npgbr3zz45bc3ihtzu2xcye5vwnzdy7pq.b32.i2p:0
+eciohu5nq7vsvwjjc52epskuk75d24iccgzmhbzrwonw6lx4gdva.b32.i2p:0
+ejlnngarmhqvune74ko7kk55xtgbz5i5ncs4vmnvjpy3l7y63xaa.b32.i2p:0
+g47cqoppu26pr4n2cfaioqx7lbdi7mea7yqhlrkdz3wjwxjxdh2a.b32.i2p:0
h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p:0
-hnbbyjpxx54623l555sta7pocy3se4sdgmuebi5k6reesz5rjp6q.b32.i2p:0
+hhfi4yqkg2twqiwezrfksftjjofbyx3ojkmlnfmcwntgnrjjhkya.b32.i2p:0
+hpiibrflqkbrcshfhmrtwfyeb7mds7a3obzwrgarejevddzamvsq.b32.i2p:0
+i4pyhsfdq4247dunel7paatdaq5gusi2hnybp2yf5wxwdnrgxaqq.b32.i2p:0
+iw6tgpmbdykffceku5da6nzf2bmz66fvp5fpcvemfu3df6aq6pga.b32.i2p:0
+jkfuajo4ayvo2rbv5qdj443q6adqmnormbhsf2f7rlp5t24xomda.b32.i2p:0
jz3s4eurm5vzjresf4mwo7oni4bk36daolwxh4iqtewakylgkxmq.b32.i2p:0
-kokkmpquqlkptu5hkmzqlttsmtwxicldr4so7wqsufk6bwf32nma.b32.i2p:0
+liu75cvktv4icbctg72w7nxbk4eibt7wamizfdii4omz7gcke5vq.b32.i2p:0
+ljsquuu3y4xje6l32p32inn6r2y6ull6oocgup6jtjrohrqxbz6a.b32.i2p:0
+lrah7acdsgopybg43shadwwiv6igezaw64i6jb5muqdg7dmhj3la.b32.i2p:0
+lzuu6mjtu7vd55d2biphicihufipoa7vyym6xfnkmmlra3tiziia.b32.i2p:0
+m6bpynxkv2ktwxkg6p2gyudjfhdupb6kuzabeqdnckkdkf4kxjla.b32.i2p:0
+m6v454xd6p3bt5swujgmveklsp7lzbkqlqqfc2p36cjlwv5dbucq.b32.i2p:0
+mlgeizrroynuhpxbzeosajt5u4ddcvynxfmcbm6kwjpaufilxigq.b32.i2p:0
+ofubxr2ir7u2guzjwyrvujicivzmvinwa36nuzlrg7tnsmebal7a.b32.i2p:0
+okfxeoh6itu4f5f43dhbzvkqwfrvm5c66lj6lvjj4q2b35i4pk4q.b32.i2p:0
+oz2ia3flpm3du2tyusulrn7h7e2eo3juzkrmn34bvnrlcrugv7ia.b32.i2p:0
+qd6jlsevsexww3wefpqs7iglxb3f63y4e6ydulfzrvwflpicmdqa.b32.i2p:0
+qddg7myylinn4tw6kdjmmp6fsyetkosnrbp2gsjx77tmkqyqv6ua.b32.i2p:0
+rizfinyses2r3or4iubs5wx66gdy6mpf73w7uobfacm2l5cral3q.b32.i2p:0
+s5hhjtmlg53bko3nwwskas7xgsmeqzy6thtsj5aa64djyrljgqaq.b32.i2p:0
sedndhv5vpcgdmykyi5st4yqhdxl3hpdtglta4do435wupahhx6q.b32.i2p:0
+tsl4dlpu2id252b6crbdnblruct664se6f2iw35fuqwa3te7wcoq.b32.i2p:0
+tugq6wa2ls2bv27pr2iy3da3k5ow3fzefbcvjcr22uc7w5vmevja.b32.i2p:0
+usztavbib756k5vqggzgkyswoj6mttihjvp3c2pa642t2mb4pvsa.b32.i2p:0
+vgu6llqbyjphml25umd5ztvyxrxuplz2g74fzbx75g3kkaetoyiq.b32.i2p:0
+wjrul5jwwb4vqdmkkrjbmly7osj6amecdpsac5xvaoqrti4nb3ha.b32.i2p:0
+wvktcp7hy4l6immhi5cxyz2dlsbhhvtcmskjemrnqehacnoap23q.b32.i2p:0
wwbw7nqr3ahkqv62cuqfwgtneekvvpnuc4i4f6yo7tpoqjswvcwa.b32.i2p:0
+xlqndzjoe5nr2nsxo6xwibh44ghyz4jfqevu62xykvemextpmjbq.b32.i2p:0
+yc4xwin5ujenvcr6ynwkz7lnmmq3nmzxvfguele6ovqqpxgjvonq.b32.i2p:0
+zdoabsg7ugzothyawodjhq54nvlofa746rxfkxpnjzj6nukmha6a.b32.i2p:0
zsxwyo6qcn3chqzwxnseusqgsnuw3maqnztkiypyfxtya4snkoka.b32.i2p:0
+zysrlpii5ftrzivfcyhdrwpeyyqddbrdefnfu5q6otk5gtugmh2a.b32.i2p:0
# manually added 2022-01 for minimal cjdns bootstrap support
[fc32:17ea:e415:c3bf:9808:149d:b5a2:c9aa]:8333
diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md
index acd767f234..1ebfb4c1de 100644
--- a/doc/bitcoin-conf.md
+++ b/doc/bitcoin-conf.md
@@ -6,6 +6,8 @@ All command-line options (except for `-?`, `-help`, `-version` and `-conf`) may
Changes to the configuration file while `bitcoind` or `bitcoin-qt` is running only take effect after restarting.
+Users should never make any configuration changes which they do not understand. Furthermore, users should always be wary of accepting any configuration changes provided to them by another source (even if they believe that they do understand them).
+
## Configuration File Format
The configuration file is a plain text file and consists of `option=value` entries, one per line. Leading and trailing whitespaces are removed.
diff --git a/doc/descriptors.md b/doc/descriptors.md
index 60cc36db07..69a5ee2715 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -11,13 +11,16 @@ Supporting RPCs are:
addresses.
- `listunspent` outputs a specialized descriptor for the reported unspent outputs.
- `getaddressinfo` outputs a descriptor for solvable addresses (since v0.18).
-- `importmulti` takes as input descriptors to import into the wallet
+- `importmulti` takes as input descriptors to import into a legacy wallet
(since v0.18).
- `generatetodescriptor` takes as input a descriptor and generates coins to it
(`regtest` only, since v0.19).
- `utxoupdatepsbt` takes as input descriptors to add information to the psbt
(since v0.19).
-- `createmultisig` and `addmultisigaddress` return descriptors as well (since v0.20)
+- `createmultisig` and `addmultisigaddress` return descriptors as well (since v0.20).
+- `importdescriptors` takes as input descriptors to import into a descriptor wallet
+ (since v0.21).
+- `listdescriptors` outputs descriptors imported into a descriptor wallet (since v22).
This document describes the language. For the specifics on usage, see the RPC
documentation for the functions mentioned above.
diff --git a/doc/i2p.md b/doc/i2p.md
index 39f65c4e5f..1599c2fe0f 100644
--- a/doc/i2p.md
+++ b/doc/i2p.md
@@ -47,9 +47,26 @@ In a typical situation, this suffices:
bitcoind -i2psam=127.0.0.1:7656
```
-The first time Bitcoin Core connects to the I2P router, its I2P address (and
-corresponding private key) will be automatically generated and saved in a file
-named `i2p_private_key` in the Bitcoin Core data directory.
+The first time Bitcoin Core connects to the I2P router, if
+`-i2pacceptincoming=1`, then it will automatically generate a persistent I2P
+address and its corresponding private key. The private key will be saved in a
+file named `i2p_private_key` in the Bitcoin Core data directory. The persistent
+I2P address is used for accepting incoming connections and for making outgoing
+connections if `-i2pacceptincoming=1`. If `-i2pacceptincoming=0` then only
+outbound I2P connections are made and a different transient I2P address is used
+for each connection to improve privacy.
+
+## Persistent vs transient I2P addresses
+
+In I2P connections, the connection receiver sees the I2P address of the
+connection initiator. This is unlike the Tor network where the recipient does
+not know who is connecting to them and can't tell if two connections are from
+the same peer or not.
+
+If an I2P node is not accepting incoming connections, then Bitcoin Core uses
+random, one-time, transient I2P addresses for itself for outbound connections
+to make it harder to discriminate, fingerprint or analyze it based on its I2P
+address.
## Additional configuration options related to I2P
@@ -85,7 +102,8 @@ one of the networks has issues.
## I2P-related information in Bitcoin Core
-There are several ways to see your I2P address in Bitcoin Core:
+There are several ways to see your I2P address in Bitcoin Core if accepting
+incoming I2P connections (`-i2pacceptincoming`):
- in the "Local addresses" output of CLI `-netinfo`
- in the "localaddresses" output of RPC `getnetworkinfo`
- in the debug log (grep for `AddLocal`; the I2P address ends in `.b32.i2p`)
diff --git a/doc/policy/packages.md b/doc/policy/packages.md
index 03c26da86b..274854ddf9 100644
--- a/doc/policy/packages.md
+++ b/doc/policy/packages.md
@@ -33,7 +33,7 @@ The following rules are enforced for all packages:
* Packages cannot have conflicting transactions, i.e. no two transactions in a package can spend
the same inputs. Packages cannot have duplicate transactions. (#20833)
-* No transaction in a package can conflict with a mempool transaction. BIP125 Replace By Fee is
+* No transaction in a package can conflict with a mempool transaction. Replace By Fee is
currently disabled for packages. (#20833)
- Package RBF may be enabled in the future.
diff --git a/doc/release-notes-25355.md b/doc/release-notes-25355.md
new file mode 100644
index 0000000000..34dd2c3687
--- /dev/null
+++ b/doc/release-notes-25355.md
@@ -0,0 +1,8 @@
+Notable changes
+===============
+
+P2P and network changes
+-----------------------
+
+- With I2P connections, a new, transient address is used for each outbound
+ connection if `-i2pacceptincoming=0`. (#25355)
diff --git a/src/.clang-tidy b/src/.clang-tidy
index 18f9035f07..9d78ccc959 100644
--- a/src/.clang-tidy
+++ b/src/.clang-tidy
@@ -1,6 +1,7 @@
Checks: '
-*,
bugprone-argument-comment,
+bugprone-use-after-move,
misc-unused-using-decls,
modernize-use-default-member-init,
modernize-use-nullptr,
@@ -12,6 +13,7 @@ readability-redundant-string-init,
'
WarningsAsErrors: '
bugprone-argument-comment,
+bugprone-use-after-move,
misc-unused-using-decls,
modernize-use-default-member-init,
modernize-use-nullptr,
diff --git a/src/Makefile.am b/src/Makefile.am
index 18c6c25b96..bf26cc9674 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -151,6 +151,7 @@ BITCOIN_CORE_H = \
external_signer.h \
flatfile.h \
fs.h \
+ headerssync.h \
httprpc.h \
httpserver.h \
i2p.h \
@@ -264,6 +265,7 @@ BITCOIN_CORE_H = \
undo.h \
util/asmap.h \
util/bip32.h \
+ util/bitdeque.h \
util/bytevectorhash.h \
util/check.h \
util/epochguard.h \
@@ -360,6 +362,7 @@ libbitcoin_node_a_SOURCES = \
dbwrapper.cpp \
deploymentstatus.cpp \
flatfile.cpp \
+ headerssync.cpp \
httprpc.cpp \
httpserver.cpp \
i2p.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 75a404c50a..8a2386a2b4 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -93,6 +93,7 @@ BITCOIN_TESTS =\
test/fs_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
+ test/headers_sync_chainwork_tests.cpp \
test/httpserver_tests.cpp \
test/i2p_tests.cpp \
test/interfaces_tests.cpp \
@@ -162,6 +163,7 @@ BITCOIN_TESTS =\
if ENABLE_WALLET
BITCOIN_TESTS += \
+ wallet/test/feebumper_tests.cpp \
wallet/test/psbt_wallet_tests.cpp \
wallet/test/spend_tests.cpp \
wallet/test/wallet_tests.cpp \
@@ -234,6 +236,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/banman.cpp \
test/fuzz/base_encode_decode.cpp \
test/fuzz/bech32.cpp \
+ test/fuzz/bitdeque.cpp \
test/fuzz/block.cpp \
test/fuzz/block_header.cpp \
test/fuzz/blockfilter.cpp \
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index 87f18371b1..f3bd543de8 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -82,7 +82,7 @@ int main(int argc, char* argv[])
// SETUP: Chainstate
const ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
- .adjusted_time_callback = static_cast<int64_t (*)()>(GetTime),
+ .adjusted_time_callback = NodeClock::now,
};
ChainstateManager chainman{chainman_opts};
@@ -195,7 +195,7 @@ int main(int argc, char* argv[])
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*new_block=*/&new_block);
+ bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
std::cerr << "duplicate" << std::endl;
@@ -210,6 +210,9 @@ int main(int argc, char* argv[])
case BlockValidationResult::BLOCK_RESULT_UNSET:
std::cerr << "initial value. Block has not yet been rejected" << std::endl;
break;
+ case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
+ std::cerr << "the block header may be on a too-little-work chain" << std::endl;
+ break;
case BlockValidationResult::BLOCK_CONSENSUS:
std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
break;
diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp
index 43d88df720..85929747be 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <mutex>
-#include <sstream>
#include <set>
#include <blockfilter.h>
@@ -13,6 +12,7 @@
#include <script/script.h>
#include <streams.h>
#include <util/golombrice.h>
+#include <util/string.h>
/// SerType used to serialize parameters in GCS filter encoding.
static constexpr int GCS_SER_TYPE = SER_NETWORK;
@@ -179,19 +179,7 @@ const std::set<BlockFilterType>& AllBlockFilterTypes()
const std::string& ListBlockFilterTypes()
{
- static std::string type_list;
-
- static std::once_flag flag;
- std::call_once(flag, []() {
- std::stringstream ret;
- bool first = true;
- for (const auto& entry : g_filter_types) {
- if (!first) ret << ", ";
- ret << entry.second;
- first = false;
- }
- type_list = ret.str();
- });
+ static std::string type_list{Join(g_filter_types, ", ", [](const auto& entry) { return entry.second; })};
return type_list;
}
diff --git a/src/chain.cpp b/src/chain.cpp
index 446bb216c2..19c35b5012 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -28,32 +28,33 @@ void CChain::SetTip(CBlockIndex& block)
}
}
-CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
- int nStep = 1;
- std::vector<uint256> vHave;
- vHave.reserve(32);
-
- if (!pindex)
- pindex = Tip();
- while (pindex) {
- vHave.push_back(pindex->GetBlockHash());
- // Stop when we have added the genesis block.
- if (pindex->nHeight == 0)
- break;
+std::vector<uint256> LocatorEntries(const CBlockIndex* index)
+{
+ int step = 1;
+ std::vector<uint256> have;
+ if (index == nullptr) return have;
+
+ have.reserve(32);
+ while (index) {
+ have.emplace_back(index->GetBlockHash());
+ if (index->nHeight == 0) break;
// Exponentially larger steps back, plus the genesis block.
- int nHeight = std::max(pindex->nHeight - nStep, 0);
- if (Contains(pindex)) {
- // Use O(1) CChain index if possible.
- pindex = (*this)[nHeight];
- } else {
- // Otherwise, use O(log n) skiplist.
- pindex = pindex->GetAncestor(nHeight);
- }
- if (vHave.size() > 10)
- nStep *= 2;
+ int height = std::max(index->nHeight - step, 0);
+ // Use skiplist.
+ index = index->GetAncestor(height);
+ if (have.size() > 10) step *= 2;
}
+ return have;
+}
- return CBlockLocator(vHave);
+CBlockLocator GetLocator(const CBlockIndex* index)
+{
+ return CBlockLocator{std::move(LocatorEntries(index))};
+}
+
+CBlockLocator CChain::GetLocator() const
+{
+ return ::GetLocator(Tip());
}
const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
diff --git a/src/chain.h b/src/chain.h
index 1c5cd3f1f6..2d3b084b9b 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -12,6 +12,7 @@
#include <primitives/block.h>
#include <sync.h>
#include <uint256.h>
+#include <util/time.h>
#include <vector>
@@ -275,6 +276,11 @@ public:
*/
bool HaveTxsDownloaded() const { return nChainTx != 0; }
+ NodeSeconds Time() const
+ {
+ return NodeSeconds{std::chrono::seconds{nTime}};
+ }
+
int64_t GetBlockTime() const
{
return (int64_t)nTime;
@@ -467,8 +473,8 @@ public:
/** Set/initialize a chain with a given tip. */
void SetTip(CBlockIndex& block);
- /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */
- CBlockLocator GetLocator(const CBlockIndex* pindex = nullptr) const;
+ /** Return a CBlockLocator that refers to the tip in of this chain. */
+ CBlockLocator GetLocator() const;
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex* FindFork(const CBlockIndex* pindex) const;
@@ -477,4 +483,10 @@ public:
CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
};
+/** Get a locator for a block index entry. */
+CBlockLocator GetLocator(const CBlockIndex* index);
+
+/** Construct a list of hash entries to put in a locator. */
+std::vector<uint256> LocatorEntries(const CBlockIndex* index);
+
#endif // BITCOIN_CHAIN_H
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index b9df1c61f0..5b5c76d51d 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -7,687 +7,855 @@
* Each line contains a BIP155 serialized (networkID, addr, port) tuple.
*/
static const uint8_t chainparams_seed_main[] = {
- 0x01,0x04,0x02,0x25,0x1e,0x90,0x22,0x49,
- 0x01,0x04,0x02,0x8a,0xae,0x9e,0x20,0x8d,
+ 0x01,0x04,0x02,0x03,0x19,0xb5,0x20,0x8d,
0x01,0x04,0x02,0x98,0x4e,0x7c,0x20,0x8d,
- 0x01,0x04,0x05,0x08,0x12,0x9a,0x20,0x8d,
- 0x01,0x04,0x05,0x2d,0x4a,0x32,0x20,0x8d,
- 0x01,0x04,0x05,0x4f,0x7b,0x03,0x20,0x8d,
- 0x01,0x04,0x05,0x66,0xa8,0xd9,0x56,0xcc,
- 0x01,0x04,0x05,0x67,0x89,0x92,0x24,0x75,
+ 0x01,0x04,0x05,0x27,0x4a,0xa6,0x20,0x8d,
+ 0x01,0x04,0x05,0x2d,0x4f,0x51,0x47,0x9c,
+ 0x01,0x04,0x05,0x35,0x10,0x80,0x20,0x8d,
+ 0x01,0x04,0x05,0x5f,0xba,0x4e,0x20,0x8d,
0x01,0x04,0x05,0x80,0x57,0x7e,0x20,0x8d,
- 0x01,0x04,0x05,0xac,0x84,0xc8,0x20,0x8d,
+ 0x01,0x04,0x05,0x85,0x41,0x52,0x20,0x8d,
+ 0x01,0x04,0x05,0x92,0x14,0xe5,0x20,0x8d,
+ 0x01,0x04,0x05,0xb4,0x29,0x77,0x20,0x8d,
0x01,0x04,0x05,0xbc,0x3e,0x12,0x20,0x8d,
- 0x01,0x04,0x05,0xfe,0x65,0xe2,0x20,0x8e,
- 0x01,0x04,0x08,0xd2,0x12,0x38,0x20,0x8d,
- 0x01,0x04,0x08,0xd2,0x5c,0x20,0x20,0x8d,
- 0x01,0x04,0x0e,0x0d,0x22,0xe1,0x3f,0x35,
- 0x01,0x04,0x0e,0x27,0x97,0xa7,0x20,0x8d,
- 0x01,0x04,0x12,0xc4,0x4f,0x6c,0x20,0x8d,
- 0x01,0x04,0x12,0xda,0x8b,0x3a,0xbc,0xcd,
- 0x01,0x04,0x14,0xb8,0x0f,0x74,0x20,0xf1,
- 0x01,0x04,0x17,0xaf,0x00,0xdc,0x20,0x8d,
+ 0x01,0x04,0x05,0xc7,0xad,0x42,0x20,0x8d,
+ 0x01,0x04,0x05,0xff,0x61,0x19,0x20,0x8d,
+ 0x01,0x04,0x05,0xff,0x67,0xb4,0x20,0x8d,
+ 0x01,0x04,0x08,0xd1,0x46,0x4d,0x20,0x8d,
+ 0x01,0x04,0x08,0xd1,0x69,0x8a,0x20,0x8d,
+ 0x01,0x04,0x12,0xa2,0xd0,0x99,0xbc,0xcc,
+ 0x01,0x04,0x17,0xaf,0x00,0xc8,0x20,0x8d,
+ 0x01,0x04,0x17,0xaf,0x00,0xde,0x20,0x8d,
0x01,0x04,0x17,0xe9,0x6b,0x15,0x20,0x8d,
+ 0x01,0x04,0x17,0xec,0x19,0xa9,0x20,0x8d,
0x01,0x04,0x18,0x23,0x44,0xe5,0x20,0x8d,
- 0x01,0x04,0x18,0x25,0x03,0x1a,0x20,0x8d,
- 0x01,0x04,0x18,0x66,0x5b,0xcb,0x20,0x8d,
+ 0x01,0x04,0x18,0x54,0xa4,0x32,0x20,0x8d,
0x01,0x04,0x18,0x74,0x99,0x73,0x20,0x8d,
- 0x01,0x04,0x18,0x86,0x06,0xa5,0x20,0x8d,
- 0x01,0x04,0x18,0x9b,0xda,0x0d,0x20,0x8d,
- 0x01,0x04,0x18,0xa0,0x89,0xad,0x20,0x8d,
- 0x01,0x04,0x18,0xb1,0x6a,0x55,0x20,0x8d,
0x01,0x04,0x18,0xb8,0x00,0x92,0x20,0x8d,
- 0x01,0x04,0x18,0xc2,0xde,0x74,0x20,0x8d,
- 0x01,0x04,0x18,0xcd,0xd7,0xc0,0x20,0x8d,
+ 0x01,0x04,0x1b,0x21,0xa0,0xc4,0x20,0x8d,
0x01,0x04,0x1b,0x7c,0x6c,0x13,0x20,0x8d,
- 0x01,0x04,0x1f,0x0e,0x28,0x40,0x20,0x8d,
+ 0x01,0x04,0x1b,0x94,0xce,0x8c,0x20,0x8d,
+ 0x01,0x04,0x1f,0x11,0x40,0xc0,0x20,0x8d,
+ 0x01,0x04,0x1f,0x12,0x72,0x87,0x20,0x8d,
+ 0x01,0x04,0x1f,0x29,0x17,0xf9,0x20,0x8d,
+ 0x01,0x04,0x1f,0x2a,0xb0,0x8a,0x20,0x8d,
0x01,0x04,0x1f,0x2f,0xca,0x70,0x20,0x8d,
- 0x01,0x04,0x1f,0xa5,0x73,0x07,0x20,0x8d,
0x01,0x04,0x22,0x41,0x2d,0x9d,0x20,0x8d,
- 0x01,0x04,0x22,0x4e,0x30,0x68,0x20,0x8d,
0x01,0x04,0x22,0x50,0x86,0x44,0x20,0x8d,
- 0x01,0x04,0x22,0x65,0x84,0xc6,0x20,0x8d,
- 0x01,0x04,0x22,0xe3,0x44,0xd8,0x20,0x8d,
- 0x01,0x04,0x23,0x89,0xd4,0x16,0x20,0x8d,
- 0x01,0x04,0x23,0xe7,0xbe,0x86,0x20,0x8d,
- 0x01,0x04,0x25,0x01,0xd9,0x23,0x20,0x8d,
- 0x01,0x04,0x25,0x0f,0x3e,0x20,0x20,0x8d,
+ 0x01,0x04,0x22,0x7e,0x73,0x23,0x20,0x8d,
+ 0x01,0x04,0x25,0x01,0xcc,0xe7,0x20,0x8d,
+ 0x01,0x04,0x25,0x78,0x9b,0x22,0x20,0x8d,
0x01,0x04,0x25,0x8f,0x76,0xae,0x20,0x8d,
- 0x01,0x04,0x25,0xc8,0x3b,0x43,0x20,0x8d,
- 0x01,0x04,0x25,0xcd,0x09,0xa5,0x20,0x8d,
- 0x01,0x04,0x26,0x17,0xb4,0xe4,0x20,0x8d,
- 0x01,0x04,0x26,0x41,0x77,0x1a,0x20,0x8d,
+ 0x01,0x04,0x25,0xc1,0xe3,0x10,0x20,0x8d,
+ 0x01,0x04,0x25,0xdc,0x87,0x97,0x20,0x8d,
+ 0x01,0x04,0x25,0xeb,0x92,0xec,0x20,0x8d,
+ 0x01,0x04,0x26,0x7c,0x7e,0x2a,0x20,0x8d,
0x01,0x04,0x26,0x8d,0x86,0x8c,0x20,0x8d,
- 0x01,0x04,0x27,0x6d,0x7a,0x7f,0x20,0xfc,
+ 0x01,0x04,0x26,0x91,0x97,0x96,0x20,0x8d,
+ 0x01,0x04,0x28,0x73,0x89,0x1c,0x20,0x8d,
+ 0x01,0x04,0x29,0x48,0x9a,0x42,0x20,0x8d,
0x01,0x04,0x29,0x4f,0x46,0x92,0x20,0x8d,
- 0x01,0x04,0x29,0xc1,0x7a,0xbf,0x20,0x8d,
+ 0x01,0x04,0x2a,0xc1,0x37,0x87,0x20,0x8d,
0x01,0x04,0x2b,0xe1,0x3e,0x6b,0x20,0x8d,
- 0x01,0x04,0x2d,0x23,0x49,0x98,0x20,0x8d,
0x01,0x04,0x2d,0x2b,0x61,0x67,0x20,0x8d,
- 0x01,0x04,0x2d,0x3f,0x0a,0x34,0x4e,0x28,
- 0x01,0x04,0x2d,0x54,0x99,0x28,0x20,0x8d,
- 0x01,0x04,0x2d,0x5f,0x40,0xe1,0x20,0x8d,
- 0x01,0x04,0x2d,0x81,0xb4,0xd6,0x20,0x8d,
- 0x01,0x04,0x2d,0x9a,0xff,0xa2,0x20,0x8d,
- 0x01,0x04,0x2d,0xe2,0x50,0x66,0x20,0x8d,
- 0x01,0x04,0x2e,0x06,0x0a,0xe6,0x20,0x8d,
+ 0x01,0x04,0x2d,0x55,0x30,0x3a,0x20,0x8d,
+ 0x01,0x04,0x2d,0x7e,0x1a,0xe5,0x20,0x8d,
+ 0x01,0x04,0x2d,0x86,0x8e,0x28,0x20,0x8d,
+ 0x01,0x04,0x2d,0x9a,0xfc,0xa2,0x20,0x8d,
+ 0x01,0x04,0x2e,0x0d,0xd8,0xa9,0x20,0x8d,
0x01,0x04,0x2e,0x17,0x57,0xda,0x20,0x8d,
- 0x01,0x04,0x2e,0x20,0x32,0x62,0x20,0x8d,
- 0x01,0x04,0x2e,0x2f,0x54,0x55,0x20,0x8d,
+ 0x01,0x04,0x2e,0x28,0x7f,0xa4,0x20,0x8d,
0x01,0x04,0x2e,0x30,0x7e,0x3a,0x20,0x8d,
+ 0x01,0x04,0x2e,0x3b,0x0d,0x23,0x20,0x8d,
+ 0x01,0x04,0x2e,0x48,0xee,0x11,0x20,0x8d,
+ 0x01,0x04,0x2e,0x80,0x8d,0xb8,0x20,0x8d,
0x01,0x04,0x2e,0x92,0xf8,0x59,0x20,0x8d,
0x01,0x04,0x2e,0xa5,0xdd,0xd1,0x24,0x75,
0x01,0x04,0x2e,0xa6,0x8e,0x02,0x20,0x8d,
- 0x01,0x04,0x2e,0xa6,0xa2,0x2d,0x4e,0x21,
- 0x01,0x04,0x2e,0xad,0x32,0x3a,0x20,0x8d,
0x01,0x04,0x2e,0xaf,0xb2,0x03,0x20,0x8d,
- 0x01,0x04,0x2e,0xbc,0x1e,0x76,0x20,0x8d,
- 0x01,0x04,0x2e,0xdb,0x78,0x3b,0x0e,0x59,
- 0x01,0x04,0x2e,0xe5,0xee,0xbb,0x20,0x8d,
- 0x01,0x04,0x2f,0x5d,0xe6,0xab,0x20,0x8d,
- 0x01,0x04,0x2f,0x64,0xa2,0xd2,0x47,0x9c,
- 0x01,0x04,0x2f,0x90,0x6a,0xf9,0x20,0x8d,
- 0x01,0x04,0x2f,0xbc,0x46,0xcd,0x20,0x8d,
- 0x01,0x04,0x2f,0xe3,0xe2,0xf2,0x20,0x8d,
+ 0x01,0x04,0x2f,0x24,0x90,0x33,0x20,0x8d,
+ 0x01,0x04,0x2f,0xb4,0x31,0x9e,0x20,0x8d,
+ 0x01,0x04,0x31,0xe4,0x83,0x85,0x08,0xa2,
0x01,0x04,0x32,0x02,0x0d,0xa4,0x20,0x8d,
- 0x01,0x04,0x32,0x05,0x2e,0xc3,0x20,0x8d,
- 0x01,0x04,0x32,0x2d,0x80,0x1c,0x20,0x8d,
- 0x01,0x04,0x33,0x94,0x99,0x3c,0x20,0x8d,
+ 0x01,0x04,0x32,0x23,0x47,0x33,0x20,0x8d,
+ 0x01,0x04,0x32,0x35,0xfa,0xa2,0x20,0x8d,
+ 0x01,0x04,0x33,0x44,0x24,0x39,0x20,0x8d,
+ 0x01,0x04,0x33,0x8a,0x04,0x87,0x75,0x31,
0x01,0x04,0x33,0x9a,0x3e,0x67,0x20,0x8d,
- 0x01,0x04,0x33,0x9a,0x83,0x12,0x20,0x8d,
0x01,0x04,0x33,0x9e,0x96,0x9b,0x20,0x8d,
- 0x01,0x04,0x33,0x9f,0x02,0xda,0x20,0x8d,
- 0x01,0x04,0x36,0xc6,0x13,0x22,0x20,0x8d,
- 0x01,0x04,0x3a,0x69,0xa8,0x29,0x20,0x8d,
+ 0x01,0x04,0x36,0xb0,0x3f,0x10,0x20,0x8d,
0x01,0x04,0x3a,0x9e,0x00,0x56,0x20,0x8d,
- 0x01,0x04,0x3c,0xfb,0x81,0x3d,0x20,0x90,
+ 0x01,0x04,0x3b,0x8a,0x73,0x89,0x20,0x8d,
+ 0x01,0x04,0x3b,0xa7,0xbf,0x3c,0x20,0x8d,
+ 0x01,0x04,0x3c,0xcd,0xcd,0x77,0x20,0x8d,
+ 0x01,0x04,0x3c,0xea,0x7a,0xf5,0x20,0x8d,
+ 0x01,0x04,0x3c,0xf0,0xd2,0x9b,0x20,0x8d,
0x01,0x04,0x3d,0xef,0x5b,0xfa,0x20,0x8d,
- 0x01,0x04,0x3e,0x1c,0xbe,0xc2,0x20,0x8d,
- 0x01,0x04,0x3e,0x98,0x3a,0x10,0x24,0xcd,
+ 0x01,0x04,0x3e,0x4a,0x8f,0x0b,0x20,0x8d,
+ 0x01,0x04,0x3e,0x8a,0xa2,0x0c,0x20,0x8d,
+ 0x01,0x04,0x3e,0xa9,0x4a,0xe9,0x20,0x8d,
0x01,0x04,0x3e,0xab,0x81,0x20,0x20,0x8d,
- 0x01,0x04,0x3e,0xfb,0x36,0xa3,0x20,0x8d,
+ 0x01,0x04,0x3e,0xd1,0xc6,0x41,0x20,0x8d,
0x01,0x04,0x3f,0xf7,0x93,0xa6,0x20,0x8d,
- 0x01,0x04,0x40,0x21,0x44,0xb0,0x20,0x8d,
- 0x01,0x04,0x40,0x9c,0xc0,0x3d,0x20,0x8d,
- 0x01,0x04,0x40,0xbb,0xaf,0xe2,0x20,0x8d,
- 0x01,0x04,0x40,0xe9,0xf5,0x27,0x20,0x8d,
- 0x01,0x04,0x40,0xed,0x52,0x95,0x20,0x8d,
- 0x01,0x04,0x41,0x65,0xf7,0x1a,0x20,0x8d,
+ 0x01,0x04,0x40,0x62,0x4c,0x3e,0x20,0x8d,
0x01,0x04,0x42,0x1d,0x81,0xda,0x20,0x8d,
- 0x01,0x04,0x42,0x31,0xcc,0x0b,0x20,0x8d,
- 0x01,0x04,0x42,0x3a,0xf3,0xd7,0x20,0x8d,
- 0x01,0x04,0x42,0x55,0xea,0x81,0x20,0x8d,
+ 0x01,0x04,0x42,0x60,0xeb,0x1c,0x20,0x8d,
0x01,0x04,0x42,0x82,0x78,0x34,0x20,0x8d,
- 0x01,0x04,0x43,0x0a,0x79,0x91,0x20,0x8d,
- 0x01,0x04,0x43,0xd2,0xe4,0xcb,0x20,0x8d,
- 0x01,0x04,0x43,0xd5,0x57,0x15,0x20,0x8d,
+ 0x01,0x04,0x42,0xc6,0xd1,0xf3,0x20,0x8d,
+ 0x01,0x04,0x42,0xd0,0x40,0x80,0x20,0x8d,
+ 0x01,0x04,0x42,0xe1,0xe7,0x94,0x20,0x8d,
+ 0x01,0x04,0x43,0x37,0x03,0xc8,0x20,0x8d,
+ 0x01,0x04,0x43,0x3a,0xe8,0x6b,0x20,0x8d,
+ 0x01,0x04,0x43,0xd3,0x5c,0x02,0x20,0x8d,
+ 0x01,0x04,0x43,0xdf,0x77,0x7a,0x20,0x8d,
+ 0x01,0x04,0x44,0x30,0x83,0xfb,0x20,0x8d,
0x01,0x04,0x44,0xb5,0x04,0x0c,0x20,0x8d,
- 0x01,0x04,0x45,0x07,0x7c,0x92,0x20,0x8d,
- 0x01,0x04,0x45,0x08,0xaf,0xc9,0x20,0x8d,
+ 0x01,0x04,0x45,0x0e,0xb9,0x09,0x20,0x8d,
+ 0x01,0x04,0x45,0x36,0x1d,0xc1,0x20,0x8d,
0x01,0x04,0x45,0x3b,0x12,0x16,0x20,0x8d,
- 0x01,0x04,0x45,0x77,0xc1,0x09,0x20,0x8d,
- 0x01,0x04,0x45,0x82,0xc9,0x1b,0x20,0x8d,
0x01,0x04,0x45,0x83,0x65,0xb0,0x20,0x8d,
- 0x01,0x04,0x46,0x0f,0xc2,0x20,0x20,0x8d,
- 0x01,0x04,0x46,0x40,0x1b,0x0c,0x20,0x8d,
- 0x01,0x04,0x48,0x1d,0xaa,0x97,0x20,0x8d,
- 0x01,0x04,0x48,0x4a,0x22,0x63,0x20,0x8d,
- 0x01,0x04,0x48,0x85,0xb1,0x77,0x20,0x8d,
- 0x01,0x04,0x49,0xa6,0x54,0xde,0x20,0x8d,
- 0x01,0x04,0x4a,0x43,0xf0,0xcc,0x20,0x8d,
+ 0x01,0x04,0x45,0xa5,0xcd,0x8e,0x22,0x81,
+ 0x01,0x04,0x45,0xe4,0xdb,0x7c,0x20,0x8d,
+ 0x01,0x04,0x46,0x3b,0x7b,0x19,0x20,0x8d,
+ 0x01,0x04,0x46,0x3e,0x0d,0x96,0x20,0x8d,
+ 0x01,0x04,0x46,0x42,0xf8,0xaa,0x20,0x8d,
+ 0x01,0x04,0x46,0x70,0x99,0xe5,0x20,0x8d,
+ 0x01,0x04,0x46,0xa0,0xf0,0x84,0x20,0x8d,
+ 0x01,0x04,0x46,0xbe,0xb1,0xcc,0x20,0x8d,
+ 0x01,0x04,0x47,0x1c,0xbd,0xef,0x20,0x8d,
+ 0x01,0x04,0x47,0xea,0x7d,0xc6,0x20,0x8d,
+ 0x01,0x04,0x48,0x4a,0x7b,0xb3,0x20,0x8d,
+ 0x01,0x04,0x48,0xfd,0xec,0xd9,0x20,0x8d,
+ 0x01,0x04,0x49,0xdb,0xfe,0x78,0x20,0x8d,
0x01,0x04,0x4a,0x5b,0x73,0xe5,0x20,0x8d,
0x01,0x04,0x4a,0x76,0x89,0x77,0x20,0x8d,
- 0x01,0x04,0x4a,0xd5,0xfb,0xcb,0x20,0x8d,
+ 0x01,0x04,0x4a,0xc3,0xa6,0x64,0x20,0x8d,
0x01,0x04,0x4a,0xdc,0xff,0xbe,0x20,0x8d,
- 0x01,0x04,0x4c,0x0b,0x3c,0x9b,0x20,0x8d,
- 0x01,0x04,0x4c,0x42,0x90,0x7f,0x20,0x8d,
+ 0x01,0x04,0x4c,0x43,0xd3,0x6e,0x20,0x8d,
+ 0x01,0x04,0x4c,0xa9,0xa3,0x0e,0x20,0x8d,
+ 0x01,0x04,0x4d,0x20,0x79,0xa2,0x20,0x8d,
+ 0x01,0x04,0x4d,0x35,0x87,0x4a,0x20,0x8d,
0x01,0x04,0x4d,0x46,0x10,0xf5,0x20,0x8d,
0x01,0x04,0x4d,0x55,0xcc,0x95,0x20,0x8d,
- 0x01,0x04,0x4d,0x69,0x57,0x61,0x20,0x8d,
- 0x01,0x04,0x4d,0x78,0x71,0x45,0x20,0xf1,
- 0x01,0x04,0x4d,0x78,0x71,0x47,0x20,0xf1,
- 0x01,0x04,0x4d,0x78,0x7a,0x74,0x20,0xf1,
- 0x01,0x04,0x4d,0x78,0x7a,0x76,0x20,0xf1,
+ 0x01,0x04,0x4d,0x6b,0x26,0xef,0x20,0x8d,
+ 0x01,0x04,0x4d,0x78,0x1a,0x66,0x20,0x8d,
0x01,0x04,0x4d,0xa2,0xbe,0x5a,0x20,0x8d,
- 0x01,0x04,0x4d,0xa7,0xf5,0xef,0xd8,0xf8,
- 0x01,0x04,0x4d,0xe8,0x29,0xbd,0x20,0x8d,
0x01,0x04,0x4e,0x14,0xe3,0xf9,0x20,0x8d,
0x01,0x04,0x4e,0x15,0xa7,0x08,0x20,0x8d,
0x01,0x04,0x4e,0x1b,0x8b,0x0d,0x20,0x8d,
- 0x01,0x04,0x4e,0x2b,0xd0,0x19,0x20,0x8d,
- 0x01,0x04,0x4e,0x3f,0x1c,0x92,0x20,0x8d,
- 0x01,0x04,0x4e,0x48,0xe4,0xef,0x20,0x8d,
- 0x01,0x04,0x4e,0x6c,0x66,0x08,0x20,0x8d,
- 0x01,0x04,0x4e,0x81,0x00,0x27,0x20,0x8d,
- 0x01,0x04,0x4e,0x81,0xa9,0x45,0x20,0x8d,
- 0x01,0x04,0x4f,0x4d,0xb6,0xb4,0x20,0x8d,
+ 0x01,0x04,0x4e,0x5a,0x5b,0xdc,0x20,0x8d,
+ 0x01,0x04,0x4e,0x6c,0x6c,0x19,0x20,0x8d,
+ 0x01,0x04,0x4e,0x6c,0x6c,0x26,0x20,0x8d,
0x01,0x04,0x4f,0x4d,0xb6,0xb7,0x20,0x8d,
- 0x01,0x04,0x4f,0x6b,0xb2,0x3b,0x20,0x8d,
+ 0x01,0x04,0x4f,0x62,0x9f,0x07,0x2c,0x45,
+ 0x01,0x04,0x4f,0xbd,0xd3,0xc9,0x20,0x8d,
0x01,0x04,0x50,0x37,0xe1,0x9e,0x20,0x8d,
- 0x01,0x04,0x50,0x40,0xd3,0x66,0x20,0x8d,
- 0x01,0x04,0x50,0x40,0xd3,0x67,0x20,0x8d,
- 0x01,0x04,0x50,0x47,0x39,0x32,0x20,0x8d,
- 0x01,0x04,0x50,0x51,0x03,0x1b,0x20,0x8d,
- 0x01,0x04,0x50,0x52,0x37,0x2b,0x20,0x8d,
+ 0x01,0x04,0x50,0x53,0xba,0x23,0x20,0x8d,
0x01,0x04,0x50,0x58,0xac,0xe3,0xfb,0x08,
- 0x01,0x04,0x50,0x59,0xcb,0xac,0x1f,0x41,
- 0x01,0x04,0x50,0x5d,0xd5,0xf6,0x20,0x8d,
- 0x01,0x04,0x50,0x93,0x52,0xa5,0x20,0x8d,
+ 0x01,0x04,0x50,0xd1,0x57,0x67,0x24,0x75,
0x01,0x04,0x50,0xe5,0x1c,0x3c,0x20,0x8d,
- 0x01,0x04,0x50,0xf7,0xe9,0x28,0x20,0x8d,
- 0x01,0x04,0x50,0xff,0x08,0x5d,0x20,0x8d,
+ 0x01,0x04,0x51,0x07,0x10,0xb6,0x20,0x8d,
0x01,0x04,0x51,0x07,0x11,0xca,0x20,0x8d,
- 0x01,0x04,0x51,0x0a,0xf1,0xa5,0x20,0x8d,
- 0x01,0x04,0x51,0x15,0x56,0x9d,0x20,0x8d,
+ 0x01,0x04,0x51,0x13,0x0a,0x02,0x20,0x8d,
+ 0x01,0x04,0x51,0x58,0xdd,0xbe,0x20,0x8d,
0x01,0x04,0x51,0xab,0x16,0x8f,0x20,0x8d,
- 0x01,0x04,0x51,0xed,0xce,0xe0,0x20,0x97,
- 0x01,0x04,0x52,0x45,0x17,0xc3,0x20,0x8d,
+ 0x01,0x04,0x51,0xe0,0x2c,0xa4,0x20,0x8d,
+ 0x01,0x04,0x51,0xe0,0xa0,0x51,0x20,0x8d,
+ 0x01,0x04,0x52,0x01,0x44,0x36,0x20,0x8d,
+ 0x01,0x04,0x52,0x15,0xa4,0x2f,0x20,0x8d,
+ 0x01,0x04,0x52,0x40,0x74,0x05,0x20,0x8d,
+ 0x01,0x04,0x52,0x42,0x0a,0x0b,0x20,0x8d,
0x01,0x04,0x52,0x60,0x60,0x28,0x20,0x8d,
0x01,0x04,0x52,0x74,0x32,0x65,0x20,0x8d,
+ 0x01,0x04,0x52,0x81,0x44,0x3e,0x20,0x8d,
0x01,0x04,0x52,0x88,0x63,0x7a,0x20,0x8d,
- 0x01,0x04,0x52,0x95,0x61,0x19,0x44,0x9f,
0x01,0x04,0x52,0x9a,0x18,0xd1,0x20,0x8d,
- 0x01,0x04,0x52,0xa5,0xf1,0x32,0x20,0x8d,
- 0x01,0x04,0x52,0xc5,0xda,0xfd,0x20,0x8d,
- 0x01,0x04,0x52,0xca,0x44,0xe7,0x20,0x8d,
+ 0x01,0x04,0x52,0xc5,0xd7,0x7d,0x20,0x8d,
+ 0x01,0x04,0x53,0x80,0x84,0x5b,0x20,0x8d,
0x01,0x04,0x53,0x89,0x29,0x0a,0x20,0x8d,
0x01,0x04,0x53,0xd0,0x06,0xd3,0x20,0x8d,
- 0x01,0x04,0x53,0xd9,0x08,0x1f,0xad,0x84,
- 0x01,0x04,0x53,0xdc,0x6e,0x30,0x20,0x8d,
+ 0x01,0x04,0x53,0xd0,0xc1,0xf2,0x20,0x8d,
0x01,0x04,0x53,0xde,0x8a,0x55,0x20,0x8d,
+ 0x01,0x04,0x53,0xf0,0x7c,0x44,0x20,0x8d,
0x01,0x04,0x53,0xf3,0xbf,0xc7,0x20,0x8d,
- 0x01,0x04,0x54,0x16,0x8b,0x39,0x20,0x8d,
- 0x01,0x04,0x54,0x1b,0x9b,0x11,0x20,0x8d,
- 0x01,0x04,0x54,0x4b,0x1c,0xf7,0x20,0x8d,
+ 0x01,0x04,0x54,0x09,0x05,0xd3,0x20,0x8d,
+ 0x01,0x04,0x54,0x1c,0x39,0x5a,0x20,0x8d,
+ 0x01,0x04,0x54,0x26,0x03,0xf9,0x20,0x8d,
0x01,0x04,0x54,0x70,0x3c,0x10,0x20,0x8d,
- 0x01,0x04,0x54,0xd3,0x07,0x38,0x20,0x8d,
- 0x01,0x04,0x54,0xed,0x07,0xf9,0x20,0x8d,
- 0x01,0x04,0x55,0x17,0x33,0xb1,0x20,0x8d,
- 0x01,0x04,0x55,0x18,0x91,0xc6,0x20,0x8d,
- 0x01,0x04,0x55,0xb8,0x8a,0x6c,0x20,0x8d,
+ 0x01,0x04,0x54,0xd7,0x38,0x77,0x20,0x8d,
+ 0x01,0x04,0x54,0xe2,0xf3,0xaf,0x20,0x8d,
+ 0x01,0x04,0x54,0xf5,0x0e,0x49,0x20,0x8d,
+ 0x01,0x04,0x54,0xfc,0x9d,0x5a,0x47,0x9d,
+ 0x01,0x04,0x54,0xff,0xf4,0x3d,0x20,0x8d,
+ 0x01,0x04,0x55,0x17,0x18,0x7b,0x20,0x8d,
+ 0x01,0x04,0x55,0x34,0xb9,0x1d,0x21,0xda,
+ 0x01,0x04,0x55,0x3a,0x78,0xc9,0x20,0x8d,
+ 0x01,0x04,0x55,0x5d,0x60,0x12,0x20,0x8d,
+ 0x01,0x04,0x55,0xa5,0x08,0xc5,0x20,0x8d,
+ 0x01,0x04,0x55,0xad,0xa5,0x42,0x20,0x8d,
+ 0x01,0x04,0x55,0xb8,0x8f,0x69,0x20,0x8d,
+ 0x01,0x04,0x55,0xbf,0x4a,0x67,0x20,0x8d,
0x01,0x04,0x55,0xc2,0xee,0x86,0x20,0x8d,
0x01,0x04,0x55,0xc3,0x36,0x6e,0x20,0x8d,
+ 0x01,0x04,0x55,0xc3,0xc4,0x8e,0x20,0x8d,
+ 0x01,0x04,0x55,0xd0,0x45,0x0b,0x20,0x8d,
+ 0x01,0x04,0x55,0xd0,0x45,0x15,0x20,0x8d,
0x01,0x04,0x55,0xd0,0x47,0x24,0x20,0x8d,
0x01,0x04,0x55,0xd0,0x47,0x27,0x20,0x8d,
- 0x01,0x04,0x55,0xd6,0x88,0x2d,0x20,0x8d,
+ 0x01,0x04,0x55,0xd6,0x76,0x47,0x20,0x8d,
0x01,0x04,0x55,0xd6,0xa1,0xfc,0x20,0x8d,
- 0x01,0x04,0x55,0xe3,0xf5,0x80,0x20,0x8d,
- 0x01,0x04,0x56,0x12,0x22,0xf3,0x20,0x8d,
- 0x01,0x04,0x56,0x14,0x32,0xaa,0x20,0x8d,
- 0x01,0x04,0x56,0x31,0x69,0x5a,0x20,0x8d,
- 0x01,0x04,0x56,0x4c,0x07,0x84,0x20,0x8d,
+ 0x01,0x04,0x55,0xd8,0x20,0x49,0x20,0x8d,
+ 0x01,0x04,0x55,0xfe,0x62,0xdd,0x20,0x8d,
+ 0x01,0x04,0x56,0x3a,0x0b,0x98,0x20,0x8d,
+ 0x01,0x04,0x56,0x5f,0x08,0xf9,0x20,0x8d,
0x01,0x04,0x56,0x64,0x1a,0xbc,0x20,0x8d,
0x01,0x04,0x56,0x6a,0x8f,0x8f,0xd8,0x4d,
- 0x01,0x04,0x56,0x78,0x3a,0x42,0x20,0x8d,
+ 0x01,0x04,0x56,0x7c,0x91,0xb8,0x20,0x8d,
0x01,0x04,0x56,0x85,0xfb,0xef,0x22,0xc5,
- 0x01,0x04,0x56,0x95,0x08,0x17,0x22,0xc5,
- 0x01,0x04,0x57,0x4e,0xc5,0xea,0x20,0x8d,
+ 0x01,0x04,0x57,0x4f,0x5e,0xdd,0x20,0x8d,
0x01,0x04,0x57,0x78,0x08,0x05,0x4e,0x28,
- 0x01,0x04,0x57,0x79,0x25,0x9c,0x20,0x8d,
- 0x01,0x04,0x58,0x52,0xb5,0x2c,0x20,0x8d,
- 0x01,0x04,0x58,0x57,0x5d,0x34,0x06,0x9b,
- 0x01,0x04,0x58,0x62,0xeb,0x86,0x20,0x8d,
- 0x01,0x04,0x58,0x88,0xbb,0xd6,0x20,0x8d,
- 0x01,0x04,0x58,0x93,0xf4,0xfa,0x20,0x8d,
- 0x01,0x04,0x58,0x94,0x99,0x94,0x20,0x8d,
+ 0x01,0x04,0x57,0x7d,0x9d,0xdc,0x20,0x8d,
+ 0x01,0x04,0x58,0x09,0x4c,0x85,0x20,0x8d,
+ 0x01,0x04,0x58,0x5a,0xb8,0x44,0x20,0x8d,
+ 0x01,0x04,0x58,0x97,0x65,0x0e,0x13,0x88,
+ 0x01,0x04,0x58,0x97,0x65,0xfd,0x13,0x88,
+ 0x01,0x04,0x58,0xc6,0x5c,0x2f,0x20,0x8d,
+ 0x01,0x04,0x58,0xd0,0x73,0x46,0x20,0x8d,
+ 0x01,0x04,0x58,0xd2,0x0f,0x18,0x20,0x8d,
0x01,0x04,0x58,0xd4,0x2d,0xa6,0x20,0x8d,
- 0x01,0x04,0x58,0xd4,0x37,0x8a,0x20,0x8d,
- 0x01,0x04,0x59,0x26,0x60,0x99,0x24,0x39,
- 0x01,0x04,0x59,0x2f,0xa1,0x87,0x20,0x8d,
- 0x01,0x04,0x59,0x58,0x3e,0xbe,0x20,0x8d,
- 0x01,0x04,0x59,0x9e,0x20,0x2c,0x20,0x8d,
- 0x01,0x04,0x59,0xa3,0x91,0xf0,0x20,0x8d,
- 0x01,0x04,0x59,0xa3,0xf9,0xea,0x0e,0x59,
- 0x01,0x04,0x59,0xb0,0xc4,0x50,0x20,0x8d,
- 0x01,0x04,0x59,0xd8,0x15,0x60,0x20,0x8d,
- 0x01,0x04,0x5a,0x54,0xe3,0xff,0x20,0x8d,
+ 0x01,0x04,0x59,0x66,0xce,0xee,0x20,0x8d,
+ 0x01,0x04,0x59,0x67,0x6f,0x22,0x20,0x8d,
+ 0x01,0x04,0x59,0x72,0x8f,0x71,0x20,0x8d,
+ 0x01,0x04,0x59,0x86,0x3e,0x4a,0x20,0x8d,
+ 0x01,0x04,0x59,0x98,0x08,0xe7,0x20,0x8d,
+ 0x01,0x04,0x59,0xa1,0x1a,0x4e,0x20,0x8d,
+ 0x01,0x04,0x59,0xcf,0x83,0x13,0x20,0x8d,
+ 0x01,0x04,0x59,0xf8,0xc1,0xe5,0x20,0x8d,
+ 0x01,0x04,0x5a,0x03,0x30,0x3e,0x20,0x8d,
+ 0x01,0x04,0x5a,0x92,0x79,0x61,0x20,0x8d,
0x01,0x04,0x5a,0x92,0x82,0xd6,0x20,0x8d,
+ 0x01,0x04,0x5a,0xc4,0xa9,0x3a,0x20,0x8d,
0x01,0x04,0x5a,0xfa,0x09,0x01,0x20,0x8d,
0x01,0x04,0x5b,0x5d,0xc2,0x9a,0x20,0x8d,
- 0x01,0x04,0x5b,0x6a,0xbc,0xe5,0x20,0x8d,
0x01,0x04,0x5b,0x7e,0x28,0x6d,0x20,0x8d,
- 0x01,0x04,0x5b,0x89,0x7f,0x7b,0x20,0x8d,
- 0x01,0x04,0x5b,0x93,0xe8,0x62,0x20,0x8d,
- 0x01,0x04,0x5b,0x98,0x7b,0x12,0x20,0x8d,
- 0x01,0x04,0x5b,0xb2,0x11,0x78,0x20,0x8d,
0x01,0x04,0x5b,0xcc,0x63,0xb2,0x20,0x8d,
- 0x01,0x04,0x5b,0xdf,0xaf,0x0e,0x20,0x8d,
- 0x01,0x04,0x5c,0x2a,0x6e,0xf2,0x20,0x8d,
- 0x01,0x04,0x5c,0x35,0x5a,0x54,0x20,0x8d,
- 0x01,0x04,0x5c,0xdd,0x9b,0xe4,0x20,0x8d,
+ 0x01,0x04,0x5b,0xcc,0x95,0x05,0x20,0x8d,
+ 0x01,0x04,0x5b,0xce,0x11,0xc3,0x20,0x8d,
+ 0x01,0x04,0x5b,0xd1,0x33,0x83,0x20,0x8d,
+ 0x01,0x04,0x5b,0xd7,0x5b,0xfe,0x20,0x8d,
+ 0x01,0x04,0x5c,0x5b,0x1b,0x3c,0x20,0x8d,
+ 0x01,0x04,0x5c,0xdd,0x14,0xe8,0x20,0x8d,
+ 0x01,0x04,0x5c,0xff,0x55,0x1f,0x20,0x8d,
+ 0x01,0x04,0x5d,0x04,0x65,0x25,0x20,0x8d,
+ 0x01,0x04,0x5d,0x2e,0x51,0x05,0x20,0x8d,
0x01,0x04,0x5d,0x39,0x51,0xa2,0x20,0x8d,
+ 0x01,0x04,0x5d,0x49,0x27,0xc4,0x20,0x8d,
+ 0x01,0x04,0x5d,0x5a,0x52,0xe2,0x20,0x8d,
0x01,0x04,0x5d,0x5f,0x58,0x0d,0x20,0x8d,
- 0x01,0x04,0x5d,0x67,0x0d,0x01,0x20,0x8d,
0x01,0x04,0x5d,0x7b,0xb4,0xa4,0x20,0x8d,
- 0x01,0x04,0x5d,0xbe,0x75,0x1a,0x20,0x8d,
- 0x01,0x04,0x5e,0x69,0x7d,0xf0,0x20,0x8d,
- 0x01,0x04,0x5e,0x6e,0x17,0xd7,0x20,0x8d,
+ 0x01,0x04,0x5d,0xbd,0x91,0xa9,0x20,0x8d,
+ 0x01,0x04,0x5e,0x11,0xb9,0x6b,0x20,0x8d,
+ 0x01,0x04,0x5e,0x4b,0xc6,0x78,0x20,0x8d,
+ 0x01,0x04,0x5e,0x72,0xc4,0xa9,0x20,0x8d,
+ 0x01,0x04,0x5e,0x8e,0xd5,0xfa,0xd8,0xf8,
0x01,0x04,0x5e,0x9a,0x9f,0x63,0x20,0x8d,
- 0x01,0x04,0x5e,0xbd,0xa1,0x77,0x20,0x8d,
- 0x01,0x04,0x5e,0xcb,0xff,0x46,0x20,0x8d,
- 0x01,0x04,0x5e,0xe8,0xad,0x5d,0x20,0x8d,
- 0x01,0x04,0x5f,0x4f,0x7a,0x63,0x20,0x8d,
- 0x01,0x04,0x5f,0x50,0x01,0x6e,0x20,0x8d,
- 0x01,0x04,0x5f,0x53,0x49,0x1f,0x20,0x8d,
+ 0x01,0x04,0x5e,0x9e,0xf6,0xb7,0x20,0x8d,
+ 0x01,0x04,0x5e,0xef,0x91,0x20,0x20,0x8d,
+ 0x01,0x04,0x5f,0x1f,0x0c,0x16,0x20,0x8d,
+ 0x01,0x04,0x5f,0x1f,0xc4,0x0f,0x20,0x8d,
0x01,0x04,0x5f,0x6e,0x85,0xdf,0x20,0x8d,
0x01,0x04,0x5f,0x6e,0xea,0x5d,0x20,0x8d,
- 0x01,0x04,0x5f,0xa4,0x41,0xc2,0x20,0x8d,
- 0x01,0x04,0x5f,0xa5,0x08,0xb6,0x20,0x8d,
- 0x01,0x04,0x5f,0xae,0xdb,0x65,0x20,0x8d,
+ 0x01,0x04,0x5f,0xa1,0x0c,0x2d,0x20,0x8d,
0x01,0x04,0x5f,0xbf,0x82,0x64,0x20,0x8d,
+ 0x01,0x04,0x5f,0xd0,0x9e,0xa1,0x20,0x8d,
+ 0x01,0x04,0x5f,0xd5,0x91,0xda,0x20,0x8d,
0x01,0x04,0x5f,0xd6,0x35,0x9a,0x20,0x8d,
- 0x01,0x04,0x5f,0xd7,0xcd,0xb4,0x20,0x8d,
- 0x01,0x04,0x60,0x2b,0x82,0xea,0x20,0x8d,
- 0x01,0x04,0x62,0x19,0xc9,0x1f,0x20,0x8d,
- 0x01,0x04,0x62,0x80,0xf7,0xb6,0x20,0x8d,
- 0x01,0x04,0x62,0xab,0x15,0x81,0x20,0x8d,
- 0x01,0x04,0x63,0x93,0x87,0xa1,0x20,0x8d,
- 0x01,0x04,0x65,0x64,0xa3,0x76,0x20,0x87,
- 0x01,0x04,0x66,0x84,0xf5,0x10,0x20,0x8d,
- 0x01,0x04,0x66,0xb6,0xcc,0x60,0x20,0x8d,
- 0x01,0x04,0x66,0xb6,0xeb,0xf5,0x20,0x8d,
+ 0x01,0x04,0x5f,0xd6,0x35,0xa0,0x20,0x8d,
+ 0x01,0x04,0x60,0x2c,0x9c,0xc7,0x20,0x8d,
+ 0x01,0x04,0x61,0x4b,0x91,0x0c,0x20,0x8d,
+ 0x01,0x04,0x66,0x84,0xc0,0x8d,0x20,0x8d,
0x01,0x04,0x67,0x0e,0xf5,0xfa,0x20,0x8d,
- 0x01,0x04,0x67,0x2f,0xc0,0x0f,0x20,0x8d,
- 0x01,0x04,0x67,0x54,0x54,0xfa,0x20,0x8f,
- 0x01,0x04,0x67,0x63,0xa8,0x82,0x20,0x8d,
+ 0x01,0x04,0x67,0x55,0x26,0xcd,0x20,0x8d,
+ 0x01,0x04,0x67,0x58,0x5c,0x4e,0x20,0x8c,
+ 0x01,0x04,0x67,0x63,0xa8,0x64,0x20,0x8d,
0x01,0x04,0x67,0x63,0xa8,0x8c,0x20,0x8d,
- 0x01,0x04,0x67,0xc6,0xc0,0x0e,0x4e,0x28,
- 0x01,0x04,0x67,0xe8,0x68,0xe3,0x20,0x8d,
- 0x01,0x04,0x68,0x8f,0x02,0xc3,0x20,0x8d,
- 0x01,0x04,0x68,0xac,0xeb,0xe3,0x20,0x8d,
+ 0x01,0x04,0x67,0x63,0xaa,0xd2,0x20,0x8d,
+ 0x01,0x04,0x67,0x63,0xaa,0xdc,0x20,0x8d,
+ 0x01,0x04,0x67,0x64,0x2c,0x46,0x20,0x8d,
+ 0x01,0x04,0x67,0xb2,0xec,0x1b,0x20,0x8d,
+ 0x01,0x04,0x67,0xd1,0x0c,0x90,0x20,0x8d,
+ 0x01,0x04,0x68,0x3b,0x93,0x0f,0x20,0x8d,
+ 0x01,0x04,0x68,0x81,0xab,0x79,0x20,0x8d,
+ 0x01,0x04,0x68,0xc8,0x41,0xea,0x20,0x8d,
0x01,0x04,0x68,0xee,0xdc,0xc7,0x20,0x8d,
- 0x01,0x04,0x6b,0x0b,0x73,0x44,0x20,0x8d,
+ 0x01,0x04,0x68,0xf4,0x49,0x06,0x20,0x8d,
+ 0x01,0x04,0x6a,0x47,0x77,0xe6,0x20,0x8d,
0x01,0x04,0x6b,0xad,0xa6,0x2b,0x20,0x8d,
- 0x01,0x04,0x6c,0x04,0xd4,0x53,0x20,0x8d,
- 0x01,0x04,0x6d,0x88,0x49,0x61,0x20,0x8d,
- 0x01,0x04,0x6d,0xad,0x62,0x17,0x20,0x8d,
- 0x01,0x04,0x6d,0xbe,0x44,0x74,0x20,0x8d,
- 0x01,0x04,0x6d,0xeb,0xf6,0x3c,0x20,0x8d,
+ 0x01,0x04,0x6c,0xa1,0x16,0x4e,0x20,0x8d,
+ 0x01,0x04,0x6c,0xae,0x3f,0xea,0x20,0x8d,
+ 0x01,0x04,0x6d,0x63,0x3f,0x9f,0x20,0x8d,
+ 0x01,0x04,0x6d,0x69,0x28,0xf7,0x20,0x8d,
+ 0x01,0x04,0x6d,0x6b,0xb9,0x82,0x20,0x8d,
+ 0x01,0x04,0x6d,0x6e,0xef,0x04,0x20,0x8d,
+ 0x01,0x04,0x6d,0xad,0x29,0x2b,0x20,0x8d,
+ 0x01,0x04,0x6d,0xec,0x5a,0x75,0x20,0x8d,
0x01,0x04,0x6d,0xf8,0xce,0x0d,0x20,0x8d,
- 0x01,0x04,0x6e,0x0c,0x40,0x60,0x20,0x8d,
+ 0x01,0x04,0x6d,0xff,0x6a,0xce,0x20,0x8d,
+ 0x01,0x04,0x6f,0x5a,0x8c,0x17,0x20,0x8d,
0x01,0x04,0x6f,0x5a,0x8c,0x2e,0x20,0x8d,
- 0x01,0x04,0x6f,0x5a,0x9f,0xb8,0xc3,0x51,
- 0x01,0x04,0x71,0x6b,0xc9,0x83,0x20,0x8d,
+ 0x01,0x04,0x6f,0x5a,0x9f,0xf6,0x20,0x8d,
+ 0x01,0x04,0x70,0x76,0xbc,0x32,0x20,0x8d,
0x01,0x04,0x73,0x2f,0x8d,0xfa,0x22,0xb5,
0x01,0x04,0x74,0x3a,0xab,0x43,0x20,0x8d,
- 0x01,0x04,0x74,0x57,0x39,0xda,0x20,0x8d,
- 0x01,0x04,0x74,0xca,0xa1,0x38,0x20,0x8d,
- 0x01,0x04,0x75,0x33,0x9f,0x82,0x20,0x8d,
- 0x01,0x04,0x76,0x67,0x7e,0x8c,0x6e,0xad,
- 0x01,0x04,0x79,0x2d,0xbe,0xd2,0x20,0x8d,
- 0x01,0x04,0x79,0x63,0xc1,0x19,0x20,0x8d,
- 0x01,0x04,0x7a,0x70,0x94,0x99,0x20,0x93,
- 0x01,0x04,0x7a,0x94,0x87,0xea,0x20,0x8d,
+ 0x01,0x04,0x76,0x5c,0x6b,0x6c,0x20,0x8d,
+ 0x01,0x04,0x77,0x2a,0x37,0xcb,0x20,0x8d,
+ 0x01,0x04,0x78,0x4f,0x47,0x48,0x20,0x8d,
+ 0x01,0x04,0x79,0x63,0xf0,0x57,0x20,0x8d,
+ 0x01,0x04,0x7b,0x3c,0xd5,0xc0,0x20,0x8d,
+ 0x01,0x04,0x7c,0x9c,0x9e,0x64,0x20,0x8d,
+ 0x01,0x04,0x7c,0xde,0x7b,0xee,0x20,0x8d,
+ 0x01,0x04,0x7d,0xb2,0x06,0x74,0x20,0x8d,
0x01,0x04,0x80,0x00,0xbe,0x1a,0x20,0x8d,
0x01,0x04,0x80,0x41,0xc2,0x88,0x20,0x8d,
+ 0x01,0x04,0x81,0x0d,0xbd,0xd4,0x20,0x8d,
0x01,0x04,0x81,0x7e,0xac,0x73,0x20,0x8d,
- 0x01,0x04,0x81,0xe2,0x7d,0x0a,0x20,0x8d,
+ 0x01,0x04,0x81,0x92,0x34,0xae,0x20,0x8d,
+ 0x01,0x04,0x82,0x2c,0xa8,0xca,0x20,0x8d,
+ 0x01,0x04,0x83,0xa1,0x50,0xa6,0x20,0x8d,
0x01,0x04,0x83,0xbc,0x28,0xbf,0x20,0x8d,
0x01,0x04,0x86,0xc3,0xb9,0x34,0x20,0x8d,
- 0x01,0x04,0x87,0xb4,0x2c,0x3d,0x20,0x8d,
- 0x01,0x04,0x88,0x34,0x72,0x7b,0x20,0x8d,
+ 0x01,0x04,0x87,0x86,0xee,0x2f,0x20,0x8d,
+ 0x01,0x04,0x87,0xb4,0xda,0x3a,0x20,0x8d,
+ 0x01,0x04,0x87,0xb5,0xd7,0xed,0x20,0x8d,
+ 0x01,0x04,0x88,0x1d,0x6d,0xb4,0x20,0x8d,
+ 0x01,0x04,0x88,0x20,0xee,0x06,0x20,0x8d,
0x01,0x04,0x88,0x38,0xaa,0x60,0x20,0x8d,
- 0x01,0x04,0x89,0x74,0xd5,0x8f,0x20,0x8d,
+ 0x01,0x04,0x89,0x19,0x26,0x6c,0x20,0x8d,
0x01,0x04,0x89,0xe2,0x22,0x2e,0x20,0x8d,
- 0x01,0x04,0x8a,0x2b,0xe9,0x39,0x20,0x8d,
+ 0x01,0x04,0x8a,0xcf,0xd3,0x6a,0x20,0x8d,
0x01,0x04,0x8b,0x82,0x29,0x52,0x20,0x8d,
+ 0x01,0x04,0x8b,0x99,0xff,0x6b,0x20,0x8d,
0x01,0x04,0x8c,0xbe,0x0c,0x81,0x20,0x8d,
- 0x01,0x04,0x8e,0x04,0x69,0x4d,0x20,0x8d,
0x01,0x04,0x8e,0x36,0xb5,0xda,0x20,0x8d,
- 0x01,0x04,0x8f,0xb1,0xe7,0xf7,0x20,0x8d,
+ 0x01,0x04,0x8f,0xb1,0xe5,0x95,0x20,0x8d,
0x01,0x04,0x8f,0xb2,0x40,0x0a,0x20,0x8d,
- 0x01,0x04,0x90,0x22,0xa1,0x41,0x47,0x9d,
- 0x01,0x04,0x92,0x04,0x7c,0x86,0x20,0x8d,
+ 0x01,0x04,0x90,0x18,0xf5,0xb7,0x20,0x8d,
+ 0x01,0x04,0x90,0x7e,0x82,0xb2,0x20,0x8d,
+ 0x01,0x04,0x92,0x04,0x7c,0x81,0x20,0x8d,
+ 0x01,0x04,0x92,0x47,0x45,0x67,0x20,0x8d,
0x01,0x04,0x92,0x53,0x38,0x45,0x20,0x8d,
- 0x01,0x04,0x92,0x5a,0xc1,0x44,0x20,0x8d,
- 0x01,0x04,0x92,0xc4,0x37,0x9c,0x70,0xa1,
- 0x01,0x04,0x94,0x42,0x32,0x32,0x20,0x8f,
- 0x01,0x04,0x94,0xfb,0x01,0x14,0x20,0x97,
- 0x01,0x04,0x97,0x30,0x5f,0xd4,0x20,0x8d,
+ 0x01,0x04,0x93,0xc2,0xb1,0xa5,0x20,0x8d,
+ 0x01,0x04,0x95,0x5a,0xd6,0x4e,0x20,0x8d,
+ 0x01,0x04,0x95,0x66,0x9d,0x9c,0x20,0x8d,
+ 0x01,0x04,0x97,0xf8,0x9c,0x37,0x20,0x8d,
0x01,0x04,0x97,0xfc,0xc1,0xf5,0x20,0x8d,
- 0x01,0x04,0x98,0x2c,0x89,0x53,0x20,0x8d,
- 0x01,0x04,0x98,0x73,0xbf,0xc4,0x20,0x8d,
- 0x01,0x04,0x9a,0xdd,0x1f,0x56,0x20,0x8d,
+ 0x01,0x04,0x99,0x5c,0x5d,0x72,0x20,0x8d,
+ 0x01,0x04,0x9a,0xd3,0x06,0x02,0x20,0x8d,
0x01,0x04,0x9c,0x11,0x67,0x02,0x1f,0x98,
- 0x01,0x04,0x9d,0x8a,0x14,0x16,0x20,0x8d,
+ 0x01,0x04,0x9c,0x92,0xb1,0xdd,0x20,0x8d,
+ 0x01,0x04,0x9d,0x83,0x8f,0xad,0x20,0x8d,
0x01,0x04,0x9e,0x3a,0xbc,0x25,0x20,0x8d,
- 0x01,0x04,0x9e,0x8c,0xd1,0x4f,0x20,0x8d,
+ 0x01,0x04,0x9e,0xf8,0x27,0xef,0x20,0x8d,
0x01,0x04,0x9f,0x59,0xe6,0x80,0x20,0x8d,
- 0x01,0x04,0x9f,0xf6,0x19,0x34,0x20,0x8d,
- 0x01,0x04,0xa0,0x14,0x3b,0xfa,0x20,0xf1,
- 0x01,0x04,0xa2,0x00,0xea,0xbe,0x20,0x8d,
- 0x01,0x04,0xa2,0x3e,0x1a,0xda,0x20,0x8d,
- 0x01,0x04,0xa2,0xfa,0xbc,0xc2,0x20,0x8d,
- 0x01,0x04,0xa2,0xfb,0x46,0x52,0x20,0x8d,
- 0x01,0x04,0xa3,0x9e,0xce,0xff,0x20,0x8d,
- 0x01,0x04,0xa4,0x44,0x69,0x69,0x20,0x8d,
+ 0x01,0x04,0x9f,0xc4,0x03,0xef,0x20,0x8d,
+ 0x01,0x04,0x9f,0xe0,0xbd,0xfa,0x20,0x8d,
+ 0x01,0x04,0xa0,0x48,0x33,0x9a,0x20,0x8d,
+ 0x01,0x04,0xa1,0x1d,0xec,0x37,0x20,0x8d,
+ 0x01,0x04,0xa1,0x61,0x77,0xa6,0x20,0x8d,
+ 0x01,0x04,0xa1,0xf6,0x0b,0xe6,0x20,0x8d,
+ 0x01,0x04,0xa2,0x3e,0x12,0xe2,0x20,0x8d,
+ 0x01,0x04,0xa2,0xfa,0x7b,0xb3,0x20,0x8d,
+ 0x01,0x04,0xa2,0xfa,0xbf,0xde,0x20,0x8d,
+ 0x01,0x04,0xa2,0xfe,0x76,0x14,0x20,0x8d,
+ 0x01,0x04,0xa3,0xac,0x51,0x46,0x20,0x8d,
+ 0x01,0x04,0xa4,0x5a,0x2f,0x08,0x20,0x8d,
0x01,0x04,0xa5,0xe4,0xae,0x75,0x20,0x8d,
- 0x01,0x04,0xa6,0x3e,0x52,0x67,0x80,0x03,
- 0x01,0x04,0xa6,0x46,0x31,0x1a,0x20,0x8d,
- 0x01,0x04,0xa6,0x4e,0xf1,0x09,0x20,0x8d,
- 0x01,0x04,0xa6,0x4e,0xf1,0x19,0x20,0x8d,
- 0x01,0x04,0xa7,0x47,0x49,0xf4,0x20,0x8d,
- 0x01,0x04,0xa7,0xb3,0x93,0x9b,0x20,0x8d,
+ 0x01,0x04,0xa6,0x46,0x91,0x97,0x20,0x8d,
0x01,0x04,0xa8,0x5b,0xee,0x08,0x20,0x8d,
+ 0x01,0x04,0xaa,0xfd,0x0b,0x19,0x20,0x8d,
+ 0x01,0x04,0xab,0x67,0xaa,0x73,0x20,0x8d,
+ 0x01,0x04,0xac,0x5d,0xa6,0x87,0x20,0x8d,
+ 0x01,0x04,0xac,0x67,0xd9,0xec,0x20,0x8d,
0x01,0x04,0xac,0x69,0x15,0xd8,0x20,0x8d,
- 0x01,0x04,0xac,0x75,0x69,0x5f,0x20,0x8d,
- 0x01,0x04,0xad,0x17,0x67,0x1e,0x1f,0x40,
- 0x01,0x04,0xad,0xcd,0x5c,0x97,0xd6,0x15,
- 0x01,0x04,0xad,0xcd,0x5c,0x9a,0xd6,0x15,
- 0x01,0x04,0xad,0xcd,0x5c,0x9d,0xd6,0x15,
+ 0x01,0x04,0xac,0x70,0x99,0x5f,0x20,0x8d,
+ 0x01,0x04,0xad,0x03,0xda,0x5b,0x20,0x8d,
+ 0x01,0x04,0xad,0x0c,0x77,0x85,0x20,0x8d,
+ 0x01,0x04,0xad,0x22,0x7f,0xb5,0x20,0x8d,
+ 0x01,0x04,0xad,0x4c,0x7b,0xad,0x20,0x8d,
+ 0x01,0x04,0xad,0xb0,0xc6,0x44,0x20,0x8d,
0x01,0x04,0xad,0xd0,0x98,0xda,0x20,0x8d,
0x01,0x04,0xad,0xf1,0xe3,0xf3,0x20,0x8d,
- 0x01,0x04,0xae,0x03,0x04,0xe8,0x20,0x8d,
- 0x01,0x04,0xae,0x11,0x0b,0x16,0x20,0x8d,
- 0x01,0x04,0xae,0x58,0xf1,0xa7,0x20,0x8d,
- 0x01,0x04,0xae,0x72,0x66,0x29,0x20,0x8d,
+ 0x01,0x04,0xad,0xf6,0x1b,0x07,0x20,0x8d,
+ 0x01,0x04,0xad,0xff,0xf0,0xcd,0x20,0x8d,
+ 0x01,0x04,0xae,0x1e,0x2f,0x0f,0x20,0x8d,
0x01,0x04,0xae,0x72,0xfa,0x56,0x20,0x8d,
+ 0x01,0x04,0xae,0x8a,0x23,0xe5,0x20,0x8d,
0x01,0x04,0xae,0x8e,0xbf,0x88,0x20,0x8d,
- 0x01,0x04,0xaf,0x27,0x48,0x57,0x20,0x8d,
- 0x01,0x04,0xb0,0x0c,0x10,0x87,0x20,0x8d,
- 0x01,0x04,0xb0,0x25,0x17,0x1e,0x20,0x8d,
- 0x01,0x04,0xb0,0x3e,0xb3,0xdd,0x20,0x8d,
+ 0x01,0x04,0xb0,0x0a,0x8f,0xbe,0x20,0x8d,
0x01,0x04,0xb0,0x4a,0x88,0xed,0x20,0x8d,
- 0x01,0x04,0xb0,0x63,0x06,0xe2,0x20,0x8d,
+ 0x01,0x04,0xb0,0x76,0xdc,0x1d,0x20,0x8d,
+ 0x01,0x04,0xb0,0x7e,0x74,0x07,0x20,0x8d,
+ 0x01,0x04,0xb0,0x7e,0xa7,0x0a,0x20,0x8d,
0x01,0x04,0xb0,0xd4,0xb9,0x99,0x20,0x8d,
+ 0x01,0x04,0xb0,0xeb,0xd1,0xba,0x20,0x8d,
0x01,0x04,0xb1,0x51,0xec,0x75,0x20,0x8d,
- 0x01,0x04,0xb2,0x13,0x6a,0x1a,0x20,0x8d,
- 0x01,0x04,0xb2,0x15,0x76,0xb2,0x20,0x8d,
- 0x01,0x04,0xb2,0x21,0xe8,0x45,0x20,0x8d,
- 0x01,0x04,0xb2,0x4f,0x54,0x8b,0x20,0x8d,
+ 0x01,0x04,0xb1,0x59,0xcd,0x46,0x20,0x8d,
+ 0x01,0x04,0xb2,0x30,0xa8,0x0c,0x20,0x8d,
0x01,0x04,0xb2,0x7c,0xa2,0xd1,0x20,0x8d,
- 0x01,0x04,0xb2,0x84,0x02,0xf6,0x20,0x8d,
- 0x01,0x04,0xb2,0x96,0x60,0x2e,0x20,0x8d,
- 0x01,0x04,0xb2,0xa2,0xd4,0x2c,0x20,0x8d,
- 0x01,0x04,0xb2,0xc1,0xe2,0x78,0x20,0x8d,
+ 0x01,0x04,0xb2,0x9f,0x62,0x85,0x20,0x8d,
+ 0x01,0x04,0xb2,0xc4,0x59,0xd1,0x20,0x8d,
0x01,0x04,0xb2,0xec,0x89,0x3f,0x20,0x8d,
+ 0x01,0x04,0xb2,0xfc,0x7b,0x18,0x20,0x8d,
+ 0x01,0x04,0xb3,0x2b,0xaa,0xba,0x20,0x8d,
0x01,0x04,0xb4,0x96,0x2e,0xbb,0x20,0x8d,
- 0x01,0x04,0xb5,0xa4,0xd2,0xe4,0x21,0x52,
- 0x01,0x04,0xb7,0x6e,0xdc,0xd2,0x76,0x5d,
- 0x01,0x04,0xb8,0x5f,0x3a,0xa6,0x20,0x90,
- 0x01,0x04,0xb8,0xa4,0x93,0x52,0xa1,0x75,
- 0x01,0x04,0xb8,0xab,0xd0,0x6d,0x20,0x8d,
- 0x01,0x04,0xb9,0x11,0x8f,0xdc,0x20,0x8d,
- 0x01,0x04,0xb9,0x15,0xd9,0x31,0x20,0x8d,
+ 0x01,0x04,0xb5,0x75,0x80,0x8c,0x20,0x8d,
+ 0x01,0x04,0xb8,0x13,0x13,0x10,0x20,0x8d,
+ 0x01,0x04,0xb9,0x15,0xd9,0x30,0x20,0x8d,
0x01,0x04,0xb9,0x19,0x30,0xb8,0x20,0x8d,
- 0x01,0x04,0xb9,0x1c,0x60,0x10,0x20,0x8d,
0x01,0x04,0xb9,0x1f,0x88,0xf6,0x20,0x8d,
+ 0x01,0x04,0xb9,0x34,0x5d,0x2d,0x20,0x8d,
0x01,0x04,0xb9,0x40,0x74,0x0f,0x20,0x8d,
0x01,0x04,0xb9,0x44,0xf9,0x5b,0x20,0x8d,
- 0x01,0x04,0xb9,0x6c,0xf7,0xbe,0x20,0x8d,
- 0x01,0x04,0xb9,0x8d,0x3c,0x24,0x20,0x8d,
- 0x01,0x04,0xb9,0x94,0x03,0xe3,0x20,0x8d,
+ 0x01,0x04,0xb9,0x62,0x36,0x14,0x20,0x8d,
+ 0x01,0x04,0xb9,0x6b,0x53,0x37,0x20,0x8d,
+ 0x01,0x04,0xb9,0x8c,0xfd,0xa9,0x20,0x8d,
0x01,0x04,0xb9,0x94,0x91,0x4a,0x20,0x8d,
- 0x01,0x04,0xb9,0x9f,0x14,0x8f,0x20,0x8d,
+ 0x01,0x04,0xb9,0xa5,0xaa,0x13,0x20,0x8d,
0x01,0x04,0xb9,0xa7,0x71,0x3b,0x20,0x8d,
0x01,0x04,0xb9,0xb9,0x1a,0x8d,0x1f,0xaf,
- 0x01,0x04,0xb9,0xbd,0x84,0xb2,0xe1,0xb4,
- 0x01,0x04,0xb9,0xcc,0xc5,0x70,0x20,0x8d,
+ 0x01,0x04,0xb9,0xc5,0xa3,0x88,0x20,0x8d,
+ 0x01,0x04,0xb9,0xd1,0x0c,0x4c,0x20,0x8d,
0x01,0x04,0xb9,0xd1,0x46,0x11,0x20,0x8d,
- 0x01,0x04,0xb9,0xdc,0x9c,0xc1,0x20,0x8d,
- 0x01,0x04,0xb9,0xee,0x81,0x71,0x20,0x8d,
+ 0x01,0x04,0xb9,0xe3,0x9c,0xe2,0x20,0x8d,
+ 0x01,0x04,0xb9,0xe9,0xbd,0xd2,0x20,0x8d,
0x01,0x04,0xb9,0xef,0xdd,0x05,0x20,0x8d,
- 0x01,0x04,0xb9,0xf4,0xd9,0x27,0x20,0x8d,
+ 0x01,0x04,0xb9,0xf4,0x64,0x6a,0x20,0x8d,
0x01,0x04,0xb9,0xfe,0x61,0xa4,0x20,0x8d,
0x01,0x04,0xba,0x21,0xa7,0x0b,0x20,0x8d,
+ 0x01,0x04,0xba,0xb0,0x62,0x25,0x20,0x8d,
+ 0x01,0x04,0xba,0xf9,0xd9,0x19,0x20,0x8d,
+ 0x01,0x04,0xba,0xfa,0x5f,0x84,0x20,0x8d,
0x01,0x04,0xbc,0x20,0x0e,0x1f,0x20,0x8e,
- 0x01,0x04,0xbc,0x2a,0x28,0xea,0x47,0x9d,
- 0x01,0x04,0xbc,0x86,0x08,0x24,0x20,0x8d,
+ 0x01,0x04,0xbc,0x23,0xa7,0x0e,0x20,0x8d,
+ 0x01,0x04,0xbc,0x44,0x2d,0x8f,0x20,0x8d,
+ 0x01,0x04,0xbc,0x75,0xc8,0xd4,0x20,0x8d,
0x01,0x04,0xbc,0x8a,0x58,0x0e,0x20,0x8d,
- 0x01,0x04,0xbc,0x9c,0x6e,0xef,0x20,0x8d,
- 0x01,0x04,0xbc,0xa5,0xf4,0x8f,0x20,0x8d,
- 0x01,0x04,0xbc,0xd5,0x44,0x26,0x20,0x8d,
- 0x01,0x04,0xbc,0xd6,0x81,0x41,0x4e,0x2c,
- 0x01,0x04,0xbc,0xf2,0x0f,0x4a,0x20,0x8d,
- 0x01,0x04,0xbc,0xf4,0x04,0x4e,0x20,0x8d,
- 0x01,0x04,0xbd,0x27,0x06,0x52,0x20,0x8d,
- 0x01,0x04,0xbd,0xcf,0x2e,0x20,0x20,0x8d,
- 0x01,0x04,0xbd,0xd4,0x79,0x4a,0x20,0x8d,
- 0x01,0x04,0xc0,0x03,0x0b,0x14,0x20,0x8d,
- 0x01,0x04,0xc0,0x41,0xaa,0x0f,0x20,0x8d,
+ 0x01,0x04,0xbc,0x97,0xed,0x9e,0x20,0x8d,
+ 0x01,0x04,0xbc,0x9a,0xec,0x31,0x20,0x8d,
+ 0x01,0x04,0xbd,0x7b,0xb1,0x80,0x20,0x8d,
+ 0x01,0x04,0xbe,0x7b,0x1b,0x0b,0x20,0x8d,
+ 0x01,0x04,0xbe,0x91,0x7f,0xfe,0x20,0x8d,
+ 0x01,0x04,0xc0,0x45,0x35,0x4d,0x20,0x8d,
0x01,0x04,0xc0,0x92,0x89,0x2c,0x20,0x8d,
- 0x01,0x04,0xc0,0xb6,0x9d,0x77,0x20,0x8d,
- 0x01,0x04,0xc0,0xbb,0x6d,0x8d,0x20,0x8d,
- 0x01,0x04,0xc0,0xe3,0x50,0x53,0x20,0x8d,
- 0x01,0x04,0xc1,0x0a,0xcb,0x17,0x20,0x8e,
- 0x01,0x04,0xc1,0x20,0x7f,0xa0,0xe4,0x6d,
- 0x01,0x04,0xc1,0x20,0x7f,0xa2,0xe4,0x6d,
- 0x01,0x04,0xc1,0x3a,0xc4,0xd4,0x20,0x8d,
- 0x01,0x04,0xc1,0x6a,0x1d,0x6a,0x20,0x8d,
- 0x01,0x04,0xc1,0x8a,0x9a,0x2b,0x20,0x8d,
- 0x01,0x04,0xc1,0xb2,0xaa,0xe8,0x20,0x8d,
+ 0x01,0x04,0xc0,0xde,0x18,0x36,0x20,0x8d,
+ 0x01,0x04,0xc0,0xde,0x93,0x8d,0x20,0x8d,
+ 0x01,0x04,0xc1,0x20,0x7f,0xa2,0xee,0x29,
+ 0x01,0x04,0xc1,0x6f,0xc6,0xbb,0x1f,0xaf,
0x01,0x04,0xc1,0xc4,0x25,0x3e,0x20,0x8d,
- 0x01,0x04,0xc1,0xde,0x82,0x0e,0x20,0x8d,
- 0x01,0x04,0xc1,0xea,0x32,0xe3,0x20,0x8d,
- 0x01,0x04,0xc2,0x0e,0xf6,0xcd,0x20,0x8d,
- 0x01,0x04,0xc2,0x87,0x87,0x45,0x20,0x8d,
+ 0x01,0x04,0xc2,0x0d,0x50,0xb9,0x3c,0x46,
0x01,0x04,0xc2,0x93,0x71,0xc9,0x20,0x8d,
0x01,0x04,0xc2,0xa5,0x1e,0x14,0x20,0x8d,
- 0x01,0x04,0xc2,0xdb,0x3e,0x17,0x20,0x8d,
+ 0x01,0x04,0xc2,0xbf,0xef,0x62,0x20,0x8d,
0x01,0x04,0xc3,0x38,0x3f,0x04,0x20,0x8d,
- 0x01,0x04,0xc3,0x86,0xb7,0xbc,0x20,0x8d,
- 0x01,0x04,0xc3,0xd0,0x67,0x1e,0x20,0xfc,
- 0x01,0x04,0xc3,0xd0,0x67,0x1f,0x20,0xfc,
+ 0x01,0x04,0xc3,0x38,0x3f,0x0a,0x20,0x8d,
+ 0x01,0x04,0xc3,0x7b,0xef,0xb9,0x20,0x8d,
+ 0x01,0x04,0xc3,0x8c,0xe2,0x9a,0x20,0x8d,
0x01,0x04,0xc6,0x01,0xe7,0x06,0x20,0x8d,
- 0x01,0x04,0xc6,0x0c,0x0e,0x88,0x20,0x8d,
- 0x01,0x04,0xc6,0x54,0xed,0x46,0x20,0x8d,
- 0x01,0x04,0xc6,0xb2,0x78,0x05,0x1f,0xb0,
- 0x01,0x04,0xc7,0x30,0x5c,0xb8,0x20,0x8d,
- 0x01,0x04,0xc7,0x44,0xc7,0x13,0x20,0x8d,
- 0x01,0x04,0xc7,0xb6,0xb8,0xcc,0x20,0x8d,
- 0x01,0x04,0xc7,0xbd,0xf2,0x8d,0x20,0x8d,
+ 0x01,0x04,0xc6,0x94,0x70,0x1b,0x20,0x8d,
+ 0x01,0x04,0xc7,0x7e,0xea,0xed,0x20,0x8d,
+ 0x01,0x04,0xc7,0xc1,0xae,0xad,0x20,0x8d,
0x01,0x04,0xc7,0xf7,0x07,0xd0,0x20,0x8d,
- 0x01,0x04,0xc8,0x7a,0xb5,0x25,0x20,0x8d,
+ 0x01,0x04,0xc8,0x7a,0xb5,0x2e,0x20,0x8d,
0x01,0x04,0xc9,0xbf,0x06,0x67,0x20,0x8d,
- 0x01,0x04,0xca,0x6b,0xdb,0x82,0x20,0x8d,
+ 0x01,0x04,0xc9,0xd4,0x24,0xd1,0x20,0x8d,
+ 0x01,0x04,0xc9,0xdd,0xea,0xc8,0x20,0x8d,
0x01,0x04,0xca,0x6c,0xd3,0x87,0x20,0x8d,
- 0x01,0x04,0xcb,0x5e,0x21,0x70,0x20,0x8d,
+ 0x01,0x04,0xca,0xa9,0x11,0xb2,0x20,0x8d,
+ 0x01,0x04,0xca,0xb1,0x18,0x8c,0x20,0x8d,
0x01,0x04,0xcb,0x82,0x30,0x75,0x22,0xb5,
0x01,0x04,0xcb,0x84,0x5e,0xc4,0x20,0x8d,
- 0x01,0x04,0xcb,0xa2,0x0d,0xb5,0x20,0x8c,
- 0x01,0x04,0xcc,0xbf,0xc9,0x2b,0x20,0x8d,
- 0x01,0x04,0xcc,0xe5,0x0a,0x5a,0x20,0x8d,
0x01,0x04,0xcd,0xb2,0x29,0x7c,0x20,0x8d,
- 0x01,0x04,0xce,0x37,0xb2,0x9d,0x20,0x8d,
- 0x01,0x04,0xce,0x7e,0xcb,0x08,0x20,0x8d,
- 0x01,0x04,0xce,0xae,0x73,0x60,0x20,0x8d,
+ 0x01,0x04,0xce,0x48,0xc9,0xe4,0x20,0x8d,
+ 0x01,0x04,0xce,0xc0,0xcb,0x00,0x20,0x8d,
0x01,0x04,0xce,0xdf,0x99,0x34,0x20,0x8d,
- 0x01,0x04,0xcf,0xbc,0x9f,0x19,0x20,0x8d,
+ 0x01,0x04,0xcf,0x86,0xd8,0x91,0x20,0x8e,
+ 0x01,0x04,0xcf,0xbc,0x9a,0x32,0x20,0x8d,
0x01,0x04,0xcf,0xe5,0x2e,0x50,0x20,0x8d,
+ 0x01,0x04,0xcf,0xff,0xc1,0x2f,0x20,0x8d,
+ 0x01,0x04,0xd0,0x68,0x5c,0x4a,0x20,0x8d,
0x01,0x04,0xd1,0x3a,0x91,0x9d,0x20,0x8d,
- 0x01,0x04,0xd1,0x7e,0x51,0x93,0x20,0x8d,
- 0x01,0x04,0xd1,0x91,0x3f,0x96,0x20,0x8d,
- 0x01,0x04,0xd1,0xd1,0x0a,0x1e,0x20,0x8d,
+ 0x01,0x04,0xd1,0x3a,0x9e,0xe8,0x20,0x8f,
+ 0x01,0x04,0xd1,0x8d,0x2b,0xf3,0x20,0x8d,
+ 0x01,0x04,0xd1,0xe2,0x8e,0x3e,0x20,0x8d,
0x01,0x04,0xd1,0xed,0x7f,0xe3,0x20,0x8d,
- 0x01,0x04,0xd4,0x63,0xe2,0x24,0x23,0x3c,
- 0x01,0x04,0xd4,0xb9,0x56,0x54,0x20,0x8d,
+ 0x01,0x04,0xd1,0xed,0x85,0x36,0x20,0x8d,
+ 0x01,0x04,0xd3,0xf8,0x5a,0x32,0x20,0x8d,
+ 0x01,0x04,0xd4,0x15,0x12,0x4e,0x20,0x8d,
+ 0x01,0x04,0xd4,0x22,0xe1,0x76,0x20,0x8d,
+ 0x01,0x04,0xd4,0x33,0x92,0x89,0x20,0x8d,
0x01,0x04,0xd4,0xe3,0xd3,0x57,0x20,0x8d,
+ 0x01,0x04,0xd5,0x00,0x45,0x4c,0x20,0x8d,
0x01,0x04,0xd5,0x05,0x24,0x3a,0x20,0x8d,
- 0x01,0x04,0xd5,0x59,0xec,0xdb,0x20,0x8d,
- 0x01,0x04,0xd5,0x5d,0x91,0xb7,0x20,0x8d,
+ 0x01,0x04,0xd5,0x2f,0x40,0x69,0x20,0x8d,
+ 0x01,0x04,0xd5,0x59,0x87,0x97,0x20,0x8d,
+ 0x01,0x04,0xd5,0x8d,0x9a,0xc9,0x20,0x8d,
+ 0x01,0x04,0xd5,0x9f,0xc6,0x2d,0x20,0x8d,
+ 0x01,0x04,0xd5,0xb8,0xf4,0x18,0x20,0x8d,
0x01,0x04,0xd5,0xd6,0x42,0xb6,0x20,0x8d,
- 0x01,0x04,0xd8,0x29,0xf9,0xb2,0x20,0x8d,
+ 0x01,0x04,0xd5,0xe2,0x7b,0x4c,0x20,0x8d,
0x01,0x04,0xd8,0x92,0xfb,0x08,0x20,0x8d,
- 0x01,0x04,0xd8,0xf9,0x46,0x16,0x20,0x8d,
- 0x01,0x04,0xd9,0x0b,0xf0,0x04,0x20,0x8d,
- 0x01,0x04,0xd9,0x0f,0xb2,0x07,0x20,0x8d,
- 0x01,0x04,0xd9,0x18,0xe9,0x74,0x20,0x8d,
- 0x01,0x04,0xd9,0x40,0x94,0x62,0xc8,0xc9,
+ 0x01,0x04,0xd8,0xba,0xee,0x0e,0x20,0x8d,
+ 0x01,0x04,0xd9,0x05,0x96,0x72,0x20,0x8d,
+ 0x01,0x04,0xd9,0x0f,0xb2,0x0b,0x20,0x8d,
+ 0x01,0x04,0xd9,0x18,0xef,0x6d,0x20,0x8d,
+ 0x01,0x04,0xd9,0x40,0x2f,0x8a,0x20,0x8d,
+ 0x01,0x04,0xd9,0x49,0x50,0x68,0x20,0x8d,
+ 0x01,0x04,0xd9,0x4f,0xb5,0x26,0x20,0x8d,
+ 0x01,0x04,0xd9,0x5c,0x37,0xf6,0x20,0x8d,
0x01,0x04,0xd9,0x71,0x79,0xa9,0x20,0x8d,
+ 0x01,0x04,0xd9,0x73,0x74,0xfa,0x20,0x8d,
+ 0x01,0x04,0xd9,0x9b,0xf4,0xaa,0x20,0x8d,
0x01,0x04,0xd9,0xaa,0x7c,0xaa,0x20,0x8d,
0x01,0x04,0xdc,0x84,0x87,0x36,0x20,0x8d,
- 0x01,0x04,0xdc,0xdd,0x3a,0x19,0x20,0x8d,
0x01,0x04,0xdc,0xe9,0xb2,0xc7,0x20,0x8d,
- 0x01,0x04,0xdd,0xdb,0x61,0x69,0x07,0xd1,
- 0x02,0x10,0x20,0x01,0x16,0x08,0x00,0x1b,0x00,0xf9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x67,0x7b,
+ 0x01,0x04,0xde,0x9a,0x6f,0x2e,0x20,0x8d,
0x02,0x10,0x20,0x01,0x16,0x20,0x05,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x60,0x01,0x39,0xaa,0x54,0x00,0x03,0xff,0xfe,0xf0,0x09,0x16,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x80,0x01,0x0f,0x71,0x54,0x00,0x04,0xff,0xfe,0x10,0x6a,0x63,0x20,0x8d,
0x02,0x10,0x20,0x01,0x1b,0xc0,0x00,0xc1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x0a,0x08,0x9a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x1c,0x02,0x01,0x1e,0x35,0x00,0xdf,0x25,0x63,0x21,0x82,0x60,0xd9,0xbe,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x10,0x04,0x1b,0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x93,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0x37,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0xaa,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x02,0x03,0xbb,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x02,0xbf,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x03,0x03,0x65,0x86,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x06,0x02,0x44,0x93,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x08,0xb9,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xd0,0x00,0x0a,0x69,0xa2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0xf0,0x00,0x00,0x00,0x00,0x00,0x62,0x69,0x74,0x63,0x6f,0x69,0x6e,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x44,0xb8,0x02,0x56,0x5d,0x11,0x02,0x16,0x3e,0xff,0xfe,0x39,0xd5,0xd4,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1b,0x62,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x07,0x08,0x03,0x02,0x0c,0x29,0xff,0xfe,0x2d,0x58,0x79,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x15,0x01,0x06,0xe2,0xd5,0x5e,0xff,0xfe,0x42,0x7a,0xe5,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x1f,0x15,0x0c,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x00,0x26,0x04,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x7c,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x75,0xe9,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x20,0x8d,
0x02,0x10,0x20,0x01,0x04,0x70,0xde,0x5a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x24,0x75,
- 0x02,0x10,0x20,0x01,0x4b,0x98,0x0d,0xc0,0x00,0x45,0x02,0x16,0x3e,0xff,0xfe,0xa2,0x95,0xcd,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4b,0xa0,0xba,0xbe,0x05,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4b,0xa0,0xff,0xff,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0x30,0xb7,0x1d,0x7b,0x6f,0xec,0x4c,0x5c,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0x08,0x8e,0xb4,0xff,0x2a,0xd0,0x69,0x9b,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0x9c,0x1c,0xcc,0x31,0x9f,0xe8,0x55,0x05,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0xa0,0xc4,0xd4,0x1f,0x04,0xc4,0x1b,0xb0,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x00,0xfd,0x76,0xc1,0xd3,0x18,0x54,0x5b,0xd9,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x01,0x00,0x00,0x00,0x00,0x76,0x76,0x80,0x90,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0x35,0x64,0x00,0x01,0xb9,0x77,0xbd,0x71,0x46,0x12,0x8e,0x40,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0xaf,0x0e,0x35,0x64,0x00,0x00,0x00,0x00,0x00,0x69,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4d,0xd0,0xaf,0x0e,0x35,0x64,0x00,0x00,0x00,0x00,0x00,0x69,0x00,0x90,0x20,0x8d,
0x02,0x10,0x20,0x01,0x4d,0xe8,0xb1,0xb2,0x00,0x01,0x00,0x00,0xde,0xad,0xbe,0xef,0x00,0x07,0x20,0x8d,
0x02,0x10,0x20,0x01,0x06,0x38,0xa0,0x00,0x41,0x40,0x00,0x00,0x00,0x00,0xff,0xff,0x01,0x91,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x06,0x48,0x28,0x00,0x01,0x31,0x4b,0x1f,0xf6,0xfc,0x20,0xf7,0xf9,0x9f,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x06,0x78,0x0c,0xc8,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x10,0x00,0x88,0x4e,0x28,
+ 0x02,0x10,0x20,0x01,0x06,0x78,0x0a,0xcc,0x00,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x20,0x01,0x06,0x7c,0x26,0xb4,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x06,0x7c,0x2d,0xb8,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x2d,0xb8,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x07,0xc0,0x23,0x10,0x00,0x00,0xf8,0x16,0x3e,0xff,0xfe,0x0d,0x4a,0xb6,0x20,0x8d,
0x02,0x10,0x20,0x01,0x07,0xc0,0x23,0x10,0x00,0x00,0xf8,0x16,0x3e,0xff,0xfe,0x6c,0x4f,0x58,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x08,0x18,0xea,0x1b,0x76,0x00,0xf0,0x53,0xaa,0xde,0xf4,0x7b,0xb7,0x01,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x08,0xf1,0x14,0x04,0x37,0x00,0x8e,0x49,0x71,0x5a,0x2e,0x09,0xb6,0x34,0x24,0xe4,
- 0x02,0x10,0x20,0x01,0x09,0x85,0x55,0xa0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x20,0x01,0x09,0x99,0x02,0x70,0x2c,0x2c,0x0c,0x8b,0x3a,0x20,0x3f,0x2f,0x31,0x8f,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x08,0x61,0x32,0x46,0x0a,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x02,0xe6,0x38,0xd7,0xba,0x27,0xeb,0xff,0xfe,0x60,0x3d,0xc1,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x64,0x61,0x78,0x11,0x04,0x89,0xd2,0xda,0x0e,0x07,0x1a,0xf7,0x20,0x8d,
0x02,0x10,0x20,0x01,0x0b,0x07,0x0a,0xc9,0x44,0x2b,0x79,0xd6,0xbb,0xbe,0xb3,0x7c,0xa7,0x83,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x16,0x00,0x00,0x00,0x02,0x08,0xa2,0xff,0xfe,0x0c,0x8a,0x2e,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x32,0x3c,0x00,0xff,0xa6,0x34,0x38,0x4f,0x18,0x49,0xf4,0xbc,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x32,0x3c,0x00,0xff,0xd2,0x17,0xc2,0xff,0xfe,0x07,0x2c,0xd9,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x3b,0xec,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x20,0x02,0x2f,0x5b,0xa5,0xf9,0x00,0x00,0x00,0x00,0x00,0x00,0x2f,0x5b,0xa5,0xf9,0x22,0xb5,
- 0x02,0x10,0x20,0x02,0xb6,0xff,0x3d,0xca,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xff,0x3d,0xca,0x6e,0xcc,
+ 0x02,0x10,0x20,0x03,0x00,0xcb,0x87,0x13,0x61,0x02,0xaa,0xa1,0x59,0xff,0xfe,0x57,0x77,0x79,0x20,0x8d,
+ 0x02,0x10,0x20,0x03,0x00,0xe0,0x37,0x0e,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x20,0x8d,
+ 0x02,0x10,0x20,0x03,0x00,0xf6,0x3f,0x10,0x67,0x00,0x4c,0x9f,0x76,0x20,0x83,0x24,0xd4,0xa7,0x20,0x8d,
0x02,0x10,0x24,0x00,0x24,0x10,0xce,0xa2,0x0d,0x00,0x41,0xbc,0xc9,0xea,0x86,0x1b,0x51,0xee,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x24,0x11,0xa3,0xe1,0x49,0x00,0x25,0x68,0x68,0x4b,0x0e,0x99,0x71,0x20,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x24,0x11,0xa3,0xe1,0x49,0x00,0x29,0x87,0xb8,0x8f,0x61,0xe0,0x84,0xfa,0x20,0x8d,
0x02,0x10,0x24,0x00,0x3b,0x00,0x00,0x20,0x00,0x0c,0xba,0xcb,0x29,0xff,0xfe,0xab,0x88,0x86,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x10,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x20,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x01,0x00,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x01,0x30,0x20,0x8d,
0x02,0x10,0x24,0x01,0xd0,0x02,0x39,0x02,0x07,0x00,0xd7,0x2c,0x5e,0x22,0x4e,0x95,0x38,0x9d,0x20,0x8d,
- 0x02,0x10,0x24,0x03,0x62,0x00,0x88,0xa0,0xfb,0x17,0xf5,0xf2,0xd8,0xb5,0xb7,0xba,0xf4,0xd3,0x20,0x8d,
- 0x02,0x10,0x24,0x05,0x98,0x00,0xb9,0x10,0x5f,0x8e,0x18,0x30,0xf6,0x30,0x2c,0xc6,0x88,0xfb,0x20,0x8d,
- 0x02,0x10,0x24,0x05,0x98,0x00,0xb9,0x70,0xc6,0x4c,0x10,0x9f,0x74,0xe7,0xae,0x5f,0x87,0xc7,0x20,0x8d,
- 0x02,0x10,0x24,0x05,0xaa,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
- 0x02,0x10,0x24,0x07,0x88,0x00,0xbc,0x61,0x22,0x02,0xd6,0x3d,0x7e,0xff,0xfe,0x6c,0xdc,0x36,0x20,0x8d,
- 0x02,0x10,0x24,0x08,0x82,0x48,0x70,0x04,0xf8,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x3c,0x20,0x8d,
+ 0x02,0x10,0x24,0x04,0x44,0x08,0x67,0x52,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x99,0x20,0x8d,
+ 0x02,0x10,0x24,0x04,0x7a,0x85,0x41,0x61,0x2b,0x00,0x49,0xa1,0x42,0x7a,0x0f,0xac,0x34,0x09,0x20,0x8d,
+ 0x02,0x10,0x24,0x05,0x98,0x00,0xb9,0x72,0xab,0x58,0x0c,0x05,0xe9,0x38,0x26,0x7e,0x02,0x71,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0xda,0x11,0x01,0x69,0x0b,0x03,0x32,0xb5,0xf9,0x01,0x9f,0x7c,0x3e,0x4b,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0xda,0x14,0x03,0x35,0xb6,0x01,0xce,0xb7,0xb4,0xfc,0xa8,0x55,0xf3,0xa5,0x20,0x8d,
+ 0x02,0x10,0x24,0x06,0xda,0x1e,0x0a,0x4e,0x8a,0x03,0x2a,0xad,0x49,0x6b,0x76,0x8d,0xe4,0x97,0x20,0x8d,
+ 0x02,0x10,0x24,0x07,0x88,0x00,0xbc,0x61,0x22,0x02,0xa0,0xc6,0x01,0x07,0x50,0x2b,0x4e,0x3b,0x20,0x8d,
0x02,0x10,0x24,0x09,0x00,0x10,0xca,0x20,0x1d,0xf0,0x02,0x24,0xe8,0xff,0xfe,0x1f,0x60,0xd9,0x20,0x8d,
- 0x02,0x10,0x24,0x0b,0x00,0x11,0x43,0xa1,0xbd,0x00,0xe5,0x89,0xf8,0xa7,0x04,0x9b,0x3b,0x86,0x20,0x8d,
- 0x02,0x10,0x24,0x0d,0x00,0x1a,0x07,0x91,0x34,0x00,0xd6,0x5d,0x64,0xff,0xfe,0x28,0x92,0x7e,0x20,0x8d,
- 0x02,0x10,0x24,0x0d,0x00,0x1a,0x07,0x91,0x34,0x00,0xd6,0x81,0xd7,0xff,0xfe,0xf6,0xa2,0x1e,0x27,0x42,
- 0x02,0x10,0x26,0x00,0x17,0x00,0x5b,0x2b,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x17,0x00,0x22,0xf1,0x64,0x1f,0x00,0xe8,0x39,0xc8,0xeb,0x1d,0xa1,0xeb,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x17,0x00,0x9c,0x5d,0x0e,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x17,0x00,0x9c,0x5d,0x0e,0xd0,0xd0,0xd6,0x01,0xd9,0x5c,0xc2,0xab,0x47,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x17,0x02,0x1c,0xe0,0x40,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x1f,0x14,0x04,0x0e,0xe3,0x01,0xd1,0x55,0xaa,0x3a,0x77,0xbe,0x96,0x0e,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x1f,0x16,0x0a,0x08,0xb9,0x01,0x1a,0xfa,0xef,0x4e,0x4c,0xe7,0x2b,0xa4,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x1f,0x1c,0x02,0xd3,0x24,0x03,0x5b,0xac,0x3f,0xc6,0x65,0x13,0x7a,0x63,0x20,0x8d,
0x02,0x10,0x26,0x00,0x21,0x04,0x10,0x03,0xc5,0xab,0xdc,0x5e,0x90,0xff,0xfe,0x18,0x1d,0x08,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x92,0xff,0xfe,0x92,0x27,0x45,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x92,0xff,0xfe,0xcf,0x61,0xb6,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0xb3,0x01,0xb6,0x20,0x8d,
0x02,0x10,0x26,0x00,0x3c,0x00,0xe0,0x02,0x2e,0x32,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x14,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x3c,0x02,0x00,0x00,0x00,0x00,0xf0,0x3c,0x92,0xff,0xfe,0x5d,0x09,0xfb,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x40,0x40,0x28,0x54,0x5e,0x00,0xc6,0xe9,0x84,0xff,0xfe,0x46,0x0e,0xe8,0x21,0xda,
+ 0x02,0x10,0x26,0x00,0x6c,0x54,0x71,0x00,0x1a,0xd1,0xbd,0xdf,0x55,0x0e,0x91,0xbe,0xf9,0xe1,0x20,0x8d,
0x02,0x10,0x26,0x00,0x88,0x05,0x24,0x00,0x01,0x4e,0x12,0xdd,0xb1,0xff,0xfe,0xf2,0x30,0x13,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x01,0x84,0x03,0x00,0x0b,0xde,0x3c,0x29,0x8e,0x94,0x1b,0xa8,0xfd,0xe3,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x01,0x8c,0x80,0x80,0x30,0x0f,0x02,0x19,0xd1,0xff,0xfe,0x75,0xdc,0x2f,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x01,0x8d,0x46,0x00,0x43,0xf1,0x20,0xe7,0xb3,0xff,0xfe,0xcf,0x0a,0x99,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x01,0x8d,0x87,0x01,0xc2,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x02,0x46,0x4d,0x7f,0x9e,0x28,0xf3,0x21,0x36,0xca,0x7a,0x71,0xc6,0x87,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x06,0x40,0xc2,0x01,0x96,0x0d,0x86,0xeb,0xf2,0x7d,0x66,0xa2,0xf2,0xc1,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0x02,0x41,0x75,0xd1,0x2b,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x20,0x8d,
0x02,0x10,0x26,0x02,0xff,0xb8,0x00,0x00,0x00,0x00,0x02,0x08,0x00,0x72,0x00,0x57,0x02,0x00,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x30,0x1f,0x1e,0xbf,0xe0,0x00,0xe2,0x3f,0x49,0xff,0xfe,0xe7,0x74,0x31,0x20,0x8d,
- 0x02,0x10,0x26,0x03,0x60,0x81,0x18,0x00,0x66,0x00,0x16,0xdd,0xa9,0xff,0xfe,0xee,0xb2,0xf3,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0x13,0x80,0x10,0x00,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x04,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2e,0x06,0x1f,0xb0,
- 0x02,0x10,0x26,0x04,0x55,0x00,0xc1,0x34,0x40,0x00,0x72,0x85,0xc2,0xff,0xfe,0x4a,0xe1,0x43,0x80,0x1d,
- 0x02,0x10,0x26,0x04,0x55,0x00,0xc1,0x34,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfc,0x80,0x1d,
+ 0x02,0x10,0x26,0x03,0x30,0x04,0x06,0xa1,0x38,0x00,0x85,0x1f,0x58,0x4d,0x7a,0xba,0xaf,0xfb,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x04,0x06,0xa1,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x02,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x04,0x07,0x0d,0x14,0x00,0x85,0x32,0x29,0x00,0xce,0x6f,0xac,0xdf,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x30,0x04,0x07,0x45,0x09,0x00,0xf0,0xd7,0x55,0x6a,0x0a,0x8c,0xce,0xd5,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x60,0x80,0xc0,0x00,0x5d,0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x4f,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x80,0x00,0xd1,0x00,0x89,0x91,0xcc,0x29,0xcc,0xff,0xfe,0x42,0x30,0x0c,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x80,0x80,0x1f,0x07,0x6f,0xdd,0x7d,0xe2,0xd9,0x69,0x78,0xc9,0xb7,0xea,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x80,0x80,0x73,0x00,0x05,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xea,0x20,0x8d,
+ 0x02,0x10,0x26,0x03,0x80,0xa0,0x07,0x03,0x40,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x01,0x80,0x00,0xf3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x18,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x3d,0x08,0x00,0x00,0x00,0x05,0xd9,0x41,0x4b,0x03,0xa0,0x93,0x13,0x1b,0x20,0x8d,
0x02,0x10,0x26,0x04,0x7c,0x00,0x01,0x20,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00,0xeb,0x24,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x0a,0x00,0x00,0x21,0x30,0x43,0xbf,0x6a,0x53,0x5e,0xdf,0xeb,0x5b,0x7b,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x1c,0xe7,0x40,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x1d,0x44,0xe0,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x26,0x1f,0x60,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x04,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x07,0xe2,0xe0,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x00,0x14,0x30,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0xa8,0x80,0x00,0x04,0x01,0xd0,0x00,0x00,0x00,0x00,0x00,0xe5,0xb0,0x00,0x20,0x8d,
0x02,0x10,0x26,0x05,0x64,0x00,0x00,0x30,0xf2,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x26,0x05,0x6f,0x80,0x00,0x00,0x00,0x07,0xfc,0x1b,0xcc,0xff,0xfe,0x8a,0xd8,0x22,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0xa1,0x40,0x20,0x76,0x82,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0xa1,0x40,0x30,0x07,0x12,0x87,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x26,0x05,0xae,0x00,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x20,0x8d,
0x02,0x10,0x26,0x05,0xc0,0x00,0x2a,0x0a,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x20,0x8d,
- 0x02,0x10,0x26,0x05,0xf7,0x00,0x00,0xc0,0x08,0x27,0x02,0x25,0x90,0xff,0xfe,0xe3,0x34,0xa6,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x1a,0x00,0x00,0x01,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x11,0x7c,0x4d,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x53,0x00,0x02,0x03,0x12,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x14,0x25,0xb5,0x20,0x8d,
- 0x02,0x10,0x26,0x07,0xf2,0xf8,0xad,0x40,0x0b,0xc1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x26,0x07,0xfa,0x18,0x3a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x01,0x1c,0x50,0x01,0x11,0x18,0xd2,0x67,0xe5,0xff,0xfe,0xe9,0xe6,0x73,0x20,0x8d,
- 0x02,0x10,0x26,0x20,0x01,0x1c,0x50,0x01,0x21,0x99,0xd2,0x67,0xe5,0xff,0xfe,0xe9,0xe6,0x73,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x21,0x9c,0x2f,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x21,0xbf,0x32,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x33,0x4d,0x1b,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0x92,0x80,0x00,0x0b,0x07,0x3b,0x02,0x50,0x56,0xff,0xfe,0x3d,0x04,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0xf2,0xc0,0xe1,0xc2,0x00,0x69,0x12,0xc3,0x7b,0xff,0xfe,0x4d,0x94,0x31,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0xf2,0xc0,0xe1,0xc2,0x00,0x69,0xec,0xb2,0x6e,0x88,0x9f,0x33,0x50,0x57,0x20,0x8d,
0x02,0x10,0x26,0x20,0x00,0x06,0x20,0x03,0x01,0x05,0x02,0xd8,0x61,0xff,0xfe,0x0f,0x08,0x53,0x20,0x8d,
0x02,0x10,0x26,0x20,0x00,0x6e,0xa0,0x00,0x00,0x01,0x00,0x42,0x00,0x42,0x00,0x42,0x00,0x42,0x20,0x8d,
- 0x02,0x10,0x28,0x03,0xcf,0x00,0x0a,0xf8,0xf2,0x00,0xb8,0x9e,0xcf,0x34,0x92,0xc7,0x2d,0x26,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x01,0x4c,0x65,0xd1,0x40,0x2c,0xbc,0x53,0xbf,0x5d,0x06,0x8a,0x21,0x36,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x07,0xf1,0xe7,0x83,0xd4,0x01,0x66,0x1c,0x67,0xff,0xfe,0xba,0x55,0x47,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x0d,0x57,0x55,0x37,0x48,0x00,0x02,0x1e,0x67,0xff,0xfe,0xa8,0xd7,0x98,0x20,0x8d,
- 0x02,0x10,0x28,0x04,0x0d,0x57,0x55,0x37,0x48,0x00,0x36,0x15,0x9e,0xff,0xfe,0x23,0xd6,0x10,0x20,0x8d,
- 0x02,0x10,0x28,0x06,0x02,0xf0,0x20,0x80,0x06,0x2a,0x08,0x6f,0x1a,0x01,0xc4,0x4f,0x17,0x94,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x10,0x28,0x83,0x82,0xbf,0x22,0x5f,0x7f,0xb7,0x8f,0x27,0x37,0x77,0x39,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x03,0xd5,0x70,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x2a,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x31,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0xa6,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0c,0xe6,0x34,0x20,0x8d,
+ 0x02,0x10,0x28,0x00,0x00,0x40,0x00,0x33,0x08,0xab,0xa0,0xe7,0xb2,0x15,0xfc,0x83,0x5c,0x31,0x20,0x8d,
+ 0x02,0x10,0x28,0x00,0x0b,0xf0,0x01,0x49,0x0f,0x4b,0xf8,0xdf,0x8d,0x7d,0x80,0x1b,0xe2,0x5e,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x01,0x4c,0x01,0x98,0x80,0xd5,0x76,0x03,0x41,0xd1,0xd3,0xfc,0xe7,0x97,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x01,0x4d,0xae,0x81,0x82,0x7b,0x99,0xa8,0x1e,0x3f,0x6d,0xb2,0x29,0xdb,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x0d,0x57,0x55,0x37,0x48,0x00,0x3e,0x7c,0x3f,0xff,0xfe,0x7b,0x80,0xaa,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x12,0xe0,0x01,0x01,0x00,0x99,0x02,0x0c,0x29,0xff,0xfe,0x29,0xd0,0x3f,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x13,0x28,0xe1,0x01,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x63,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x13,0x98,0x00,0x04,0x2a,0x03,0x02,0x15,0x5d,0xff,0xfe,0xd6,0x10,0x33,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x13,0x98,0x00,0x04,0x2a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xbc,0x03,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x16,0x30,0x00,0x10,0x10,0x03,0x00,0x00,0x0b,0x19,0xb0,0x0b,0xba,0xbe,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x17,0x68,0x20,0x01,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x6a,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x18,0x28,0xa0,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x18,0x38,0x00,0x2a,0x14,0x00,0x92,0xe2,0xba,0xff,0xfe,0x4a,0xc4,0x16,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x1c,0x10,0x00,0x02,0x07,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x17,0x56,0xcc,
0x02,0x10,0x2a,0x00,0x1f,0x40,0x50,0x01,0x01,0x08,0x5d,0x17,0x77,0x03,0xb0,0xf5,0x41,0x33,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x60,0x20,0x15,0xdd,0xee,0x00,0xc8,0xc2,0x2c,0x77,0x17,0x49,0x35,0xdb,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x23,0xc5,0xfe,0x80,0x73,0x01,0xd6,0xae,0x52,0xff,0xfe,0xd5,0x56,0xa5,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x23,0xc6,0x5c,0x91,0x58,0x08,0xc0,0x5a,0x4d,0xff,0xfe,0x65,0x9d,0x69,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x60,0x20,0x1b,0xfa,0xd4,0x00,0x02,0x0c,0x29,0xff,0xfe,0x61,0x4a,0x4c,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x60,0x20,0xb4,0x82,0x92,0x00,0x49,0x1a,0x35,0x8c,0xd8,0xf7,0x01,0xda,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0x71,0x45,0x00,0xc1,0x00,0x01,0xae,0x29,0x07,0x27,0x2b,0x87,0x0f,0x64,0x14,0x15,
+ 0x02,0x10,0x2a,0x00,0x60,0x20,0xb4,0x89,0x20,0x00,0x50,0x54,0x00,0xff,0xfe,0xfc,0x5e,0xd8,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x7c,0x80,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0x7a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x7c,0x80,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x20,0x8d,
0x02,0x10,0x2a,0x00,0x8a,0x60,0xe0,0x12,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x20,0x8d,
- 0x02,0x10,0x2a,0x00,0xa0,0x40,0x01,0x00,0x00,0xf3,0x45,0xa5,0x0a,0xc0,0xfe,0xa3,0x71,0xe1,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x04,0x88,0x20,0x00,0x98,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xae,0x40,0x24,0x0e,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xbb,0xe0,0x00,0xcc,0x00,0x00,0x62,0xa4,0x4c,0xff,0xfe,0x23,0x75,0x10,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x0c,0xa8,0x0a,0x1f,0x30,0x25,0xf9,0x49,0xe4,0x42,0xc9,0x40,0x13,0xe8,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xd4,0xe0,0x00,0x02,0xd0,0x02,0x44,0x67,0x31,0xe0,0x6f,0xa5,0xb3,0xef,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x0e,0xe2,0x12,0x00,0x19,0x00,0x08,0xd3,0xd2,0xff,0xfe,0xb1,0xbc,0x58,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x02,0x38,0x42,0x0f,0x92,0x00,0xfa,0x5a,0x1a,0x4b,0x1e,0x6a,0xfa,0xdf,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x02,0x38,0x43,0x89,0xc4,0x00,0x3b,0x26,0xd9,0x4e,0x38,0xd5,0x44,0xef,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x04,0x90,0x00,0x16,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x4b,0x00,0x80,0x7c,0x31,0x00,0xcd,0xa1,0x0c,0x6a,0x2b,0xad,0x24,0x18,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x41,0x22,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x73,0x23,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x90,0x91,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x00,0x72,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x02,0x03,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x21,0x44,0xd7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x02,0x31,0x09,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x2a,0x1c,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x2b,0x02,0x9a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf9,0x00,0x4a,0x31,0xde,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x52,0x00,0x00,0x6c,0x61,0x62,0x7a,0x61,0x74,0x6b,0x6f,0x2e,0x73,0x6b,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x63,0x80,0xff,0xfe,0x00,0x73,0x04,0xe3,0xb3,0xcc,0xa8,0x71,0x36,0xd1,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x07,0xa0,0x00,0x02,0x13,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x63,0x80,0xff,0xfe,0x00,0x73,0x10,0xfb,0xd0,0x12,0x85,0x81,0xb4,0xd7,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xa7,0x00,0x02,0x28,0x04,0xae,0x1f,0x6b,0xff,0xfe,0x9d,0x6c,0x94,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xc8,0xaa,0xac,0x00,0x89,0x50,0x54,0x00,0xff,0xfe,0xb7,0xf5,0xcb,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x07,0xc8,0xaa,0xc9,0x00,0xc9,0x50,0x54,0x00,0xff,0xfe,0xdf,0xff,0x95,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x07,0xc8,0xd0,0x01,0x01,0xc1,0x50,0x54,0x00,0xff,0xfe,0xee,0x3e,0x1a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xc8,0xd0,0x09,0x02,0xaa,0x50,0x54,0x00,0xff,0xfe,0x1b,0xa1,0x96,0x2d,0x00,
+ 0x02,0x10,0x2a,0x01,0x07,0xc8,0xff,0xfa,0x05,0x0e,0xdd,0xfe,0xc9,0x24,0xca,0x0a,0xcb,0xab,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x7e,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x59,0x66,0xdc,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x93,0xff,0xfe,0x3b,0xbb,0x5b,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x87,0x40,0x00,0x01,0xff,0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0x8c,0x6a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x9f,0x40,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x20,0x8d,
0x02,0x10,0x2a,0x01,0xcb,0x00,0x0d,0x3d,0x77,0x00,0x02,0x27,0x0e,0xff,0xfe,0x28,0xc5,0x65,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x00,0xd0,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x53,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x00,0xd0,0xbe,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x20,0x8d,
- 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x09,0xfb,0xb0,0xe0,0x54,0xf8,0x19,0x01,0x6e,0x83,0x62,0xc1,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x00,0x20,0x73,0x50,0x91,0x9c,0xb1,0xc3,0x8b,0x83,0xad,0xf9,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x03,0x01,0x70,0x10,0xb8,0x7d,0xe1,0x4b,0xce,0xa9,0xb9,0x98,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x04,0x8b,0x2d,0x10,0x94,0xf2,0x4d,0x5c,0xca,0x5f,0xbf,0x49,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x0a,0x05,0x30,0xa0,0xa0,0xf4,0x65,0x0a,0xf5,0xbe,0x1b,0x90,0x75,0x20,0x8d,
0x02,0x10,0x2a,0x01,0x0e,0x0a,0x0a,0xa7,0xc8,0xc0,0x96,0x79,0xaf,0xfa,0xb6,0xe5,0xef,0xc7,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x13,0xb8,0xf0,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x01,0x68,0x63,0x28,0x00,0x00,0x02,0xa8,0x2c,0xff,0xfe,0x68,0xe3,0x2c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x11,0x10,0x0c,0x00,0x70,0xcb,0xc8,0x9e,0x31,0x4b,0x77,0x16,0x26,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x34,0xee,0x78,0x30,0x60,0x02,0x30,0x48,0xff,0xfe,0x81,0xf1,0xc6,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x14,0xa9,0x67,0x00,0x0a,0x00,0x27,0xff,0xfe,0x4e,0x82,0xb6,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x46,0x39,0x0f,0x00,0x10,0xa7,0xe9,0x65,0x50,0x9a,0x7a,0x4a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x7c,0x92,0x51,0x00,0x02,0x11,0x32,0xff,0xfe,0xae,0x15,0x2d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x86,0xbf,0xf1,0x00,0x31,0x78,0xd7,0x00,0xd4,0x4d,0x6b,0xb1,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x10,0x94,0x87,0xa2,0x00,0xed,0xc1,0x93,0xa4,0x09,0x45,0x9a,0x92,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x68,0x42,0x0b,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x68,0x63,0x28,0x00,0x00,0x4a,0x21,0x0b,0xff,0xfe,0x26,0x38,0xc3,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x68,0x67,0x6e,0x00,0x00,0xe6,0x5f,0x01,0xff,0xfe,0x09,0x35,0x91,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x17,0x48,0xf3,0x9f,0x58,0x72,0xde,0xad,0xbe,0xef,0xb1,0xac,0xc0,0xfe,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x80,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x05,0x17,0x10,0xb6,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x21,0x68,0xa3,0x79,0xd1,0x00,0x96,0xde,0x80,0xff,0xfe,0xa3,0xfd,0x00,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x27,0x80,0x90,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x27,0x80,0x90,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x27,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x1a,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x2e,0x02,0x39,0x00,0x54,0x00,0xa0,0x99,0xe1,0xff,0xfe,0xb6,0x0d,0x0e,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x03,0x90,0x90,0x00,0x00,0x00,0xaa,0xa1,0x59,0xff,0xfe,0x43,0xb5,0x7b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x2f,0x05,0x66,0x0e,0x8b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x00,0x58,0x00,0x97,0x7d,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x6d,0x40,0x30,0x5e,0x06,0x01,0xde,0xa6,0x32,0xff,0xfe,0x44,0x4b,0x25,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x6d,0x40,0x30,0x73,0x0c,0x01,0xde,0xa6,0x32,0xff,0xfe,0x44,0x4b,0x25,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x7a,0x01,0x00,0x00,0x00,0x00,0x00,0x91,0x02,0x28,0x00,0x45,0x01,0x30,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x7a,0xa0,0x16,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xdc,0x8d,0xe0,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x7b,0x40,0x3e,0x4d,0x99,0x8d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x7b,0x40,0x59,0x2f,0xa1,0x87,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x83,0x88,0xe5,0xc6,0xd3,0x80,0x02,0x01,0x2e,0xff,0xfe,0x82,0xb3,0xcc,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x09,0xa0,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0xa3,0x11,0x81,0x43,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x20,0xa1,
- 0x02,0x10,0x2a,0x02,0x0a,0xf8,0xfa,0xb0,0x08,0x08,0x00,0x85,0x02,0x34,0x01,0x45,0x01,0x32,0x20,0x8d,
- 0x02,0x10,0x2a,0x02,0x0e,0x00,0xff,0xf0,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0xfc,
- 0x02,0x10,0x2a,0x02,0x0e,0x00,0xff,0xf0,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x20,0xfc,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0x59,0x28,0x00,0x89,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0xc3,0xb5,0xf5,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x83,0x08,0x80,0x87,0xaa,0x00,0x9e,0xa8,0x01,0xb2,0xef,0x98,0x56,0xbf,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x84,0x2a,0x01,0xdf,0x8a,0x01,0x1e,0x1b,0x0d,0xff,0xfe,0x0b,0x23,0x6d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x4d,0x14,0xd6,0x00,0x01,0x02,0xc0,0x08,0xff,0xfe,0x8f,0xb3,0xb2,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x5a,0x94,0xcd,0xf0,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x5f,0x3b,0x9d,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x67,0x78,0x33,0x00,0x01,0x72,0x85,0xc2,0xff,0xfe,0x2c,0x21,0xe9,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xaa,0x14,0x23,0x80,0xb3,0x00,0x40,0x40,0xbe,0x88,0x8b,0x01,0x0d,0x38,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x06,0x20,0x44,0x98,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x06,0x20,0x82,0x12,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x06,0x30,0x08,0x23,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x00,0x00,0x49,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x14,0xd4,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x14,0x41,0x99,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x24,0x61,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x20,0x26,0x66,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xc2,0x07,0x30,0x02,0x74,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x02,0x0e,0x98,0x00,0x20,0x15,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x47,0x00,0xf1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x06,0x41,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x40,0x00,0x00,0x06,0xf8,0x14,0x54,0x8b,0x17,0xff,0xfe,0x31,0xb6,0x4a,0x20,0x8d,
0x02,0x10,0x2a,0x03,0x60,0x00,0x08,0x70,0x00,0x00,0x00,0x46,0x00,0x23,0x00,0x87,0x02,0x18,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x73,0x80,0x30,0x15,0x05,0x24,0xaf,0xc5,0xd3,0xbc,0x7c,0x66,0x8f,0x94,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x0e,0xc0,0x00,0x00,0x09,0x28,0x8c,0x00,0x93,0xff,0xfe,0x84,0xa0,0x07,0x20,0x8d,
- 0x02,0x10,0x2a,0x03,0x0e,0xc0,0x00,0x00,0x09,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x01,0x20,0x8d,
- 0x02,0x10,0x2a,0x04,0x21,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xaa,0x20,0x8d,
- 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x01,0x02,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
- 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x03,0xc4,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x94,0xe0,0xff,0xff,0x01,0x85,0x02,0x43,0x02,0x18,0x00,0x00,0x00,0x19,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x01,0x00,0xe0,0x00,0x00,0x00,0x00,0x03,0x97,0x60,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xf0,0x00,0x00,0x00,0x00,0x01,0x63,0x30,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xf0,0x00,0x00,0x00,0x00,0x01,0x8a,0xd0,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x0f,0x3e,0x20,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xe2,0xc0,0x13,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x0e,0xc0,0x00,0x00,0x09,0x28,0x00,0x00,0x00,0x00,0x07,0x01,0x07,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x03,0xc4,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8e,
+ 0x02,0x10,0x2a,0x04,0x52,0xc0,0x30,0x07,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x20,0x8d,
0x02,0x10,0x2a,0x04,0xbc,0x40,0x1d,0xc3,0x00,0x8d,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x01,0x20,0x8d,
0x02,0x10,0x2a,0x05,0x15,0x00,0x07,0x02,0x00,0x00,0x1c,0x00,0x40,0xff,0xfe,0x00,0x00,0x0c,0x20,0x8d,
- 0x02,0x10,0x2a,0x06,0xdd,0x00,0x00,0x10,0x00,0x03,0x02,0x25,0x90,0xff,0xfe,0x32,0x64,0xcc,0x20,0x8d,
- 0x02,0x10,0x2a,0x06,0xdd,0x00,0x00,0x01,0x00,0x22,0x02,0x25,0x90,0xff,0xfe,0x0e,0xbd,0x48,0x20,0x8d,
- 0x02,0x10,0x2a,0x07,0x6b,0x47,0x01,0x00,0x04,0x64,0x00,0x00,0x00,0x00,0x93,0x57,0xff,0xda,0x20,0x8d,
- 0x02,0x10,0x2a,0x07,0xa8,0x80,0x46,0x01,0x10,0x62,0xb4,0xb4,0xbd,0x2a,0x39,0xd4,0x7a,0xcf,0xc8,0xc9,
+ 0x02,0x10,0x2a,0x05,0x35,0x80,0xd1,0x01,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0x35,0x80,0xdb,0x0b,0x16,0x00,0xc4,0x89,0x76,0xed,0x31,0x3d,0x0b,0x33,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x01,0x81,0x27,0xaf,0xa7,0xda,0xf9,0xd9,0x1b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x01,0xf6,0xab,0xdd,0x5e,0x40,0x39,0xb4,0x6c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x14,0x0a,0x55,0x40,0x03,0x65,0x23,0x50,0xa1,0x01,0x52,0xe8,0x8c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xd0,0x1a,0x0b,0x7b,0x3c,0x01,0x8b,0xf7,0xae,0x14,0xaf,0xb3,0x33,0xae,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xf4,0x80,0x18,0x00,0x06,0x97,0x54,0x00,0x02,0xff,0xfe,0xb6,0xc3,0x6d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x06,0xe0,0x40,0x76,0x03,0x29,0x18,0xc6,0xef,0x46,0x4e,0x9f,0xe5,0x73,0xec,0x20,0x8d,
0x02,0x10,0x2a,0x07,0xab,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x09,0x46,0x20,0x8d,
- 0x02,0x10,0x2a,0x07,0xab,0xc4,0x00,0x00,0x00,0x00,0x00,0x89,0x02,0x34,0x01,0x80,0x01,0x94,0x20,0x8d,
0x02,0x10,0x2a,0x09,0x26,0x81,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x20,0x8d,
0x02,0x10,0x2a,0x0a,0xc8,0x01,0x00,0x01,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x83,0x20,0x8d,
- 0x02,0x10,0x2a,0x0b,0xf3,0x00,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
- 0x02,0x10,0x2a,0x0c,0x59,0xc0,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa2,0x0e,0xe1,0x3a,
+ 0x02,0x10,0x2a,0x0c,0x5a,0x80,0x12,0x10,0xa8,0x00,0x6a,0xf7,0x28,0xff,0xfe,0xe5,0x6b,0x3a,0x20,0x8d,
0x02,0x10,0x2a,0x0d,0x56,0x00,0x00,0x24,0x0a,0x8e,0x00,0x00,0x00,0x00,0x00,0x00,0xa9,0x1e,0xd8,0x4d,
- 0x02,0x10,0x2a,0x0d,0xeb,0x00,0x80,0x05,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x20,0x8d,
- 0x02,0x10,0x2a,0x10,0x47,0x40,0x00,0x45,0x00,0x01,0xa0,0x13,0xd1,0xff,0xfe,0x85,0x36,0xe3,0x20,0x8d,
- 0x02,0x10,0x2a,0x10,0x8b,0x40,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x20,0x8f,
+ 0x02,0x10,0x2a,0x0d,0x7c,0x40,0x30,0x00,0x0b,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0d,0x83,0x40,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0f,0xdf,0x00,0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x62,0x20,0x8d,
+ 0x02,0x10,0x2a,0x10,0x37,0x81,0x16,0xb9,0x00,0x01,0xfe,0x3f,0xdb,0xff,0xfe,0x04,0x2d,0x4c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x10,0x37,0x81,0x08,0x4b,0x00,0x01,0xb1,0x23,0x63,0x06,0x94,0x3a,0xf0,0x9b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x10,0xd2,0x00,0x00,0x01,0x00,0x33,0xa6,0xbf,0x01,0xff,0xfe,0x6a,0x46,0xa9,0x20,0x8d,
+ 0x02,0x10,0x2c,0x0f,0xf4,0xc0,0x22,0x02,0x20,0xb0,0x26,0x1c,0x04,0xff,0xfe,0x14,0xda,0xa0,0x20,0x8d,
0x02,0x10,0x2c,0x0f,0xf8,0xf0,0xda,0x51,0x00,0x00,0x70,0xc3,0xee,0xa9,0x97,0x17,0x95,0x79,0x20,0x8d,
- 0x04,0x20,0xd1,0xbb,0x02,0x8d,0x4d,0xd5,0x6a,0x20,0xc0,0xf9,0x16,0x2b,0x84,0x22,0x66,0xe0,0x89,0x45,0x60,0x37,0x52,0xe2,0x0b,0xa5,0xb4,0xf8,0x26,0xb3,0x8f,0x5a,0x30,0xed,0x20,0x8d,
- 0x04,0x20,0xd2,0x59,0x3b,0xd7,0x14,0x7e,0xd0,0x98,0xfe,0x9e,0xa5,0x69,0xf4,0x26,0x6d,0x72,0x6f,0xc3,0x76,0xce,0x1d,0x40,0x41,0xa2,0xa1,0xaf,0xf9,0x6e,0x57,0x2d,0x9d,0xc3,0x20,0x8d,
- 0x04,0x20,0xdf,0xd9,0xed,0x59,0xbf,0x1e,0x77,0x48,0x3c,0x13,0x3b,0xc5,0xc8,0x15,0x86,0x88,0x68,0xf0,0x08,0xe9,0xee,0x9b,0x3d,0xa4,0x33,0x0a,0x68,0x67,0x86,0x9d,0xe2,0x83,0x20,0x8d,
0x04,0x20,0xe9,0xbf,0xa7,0xbd,0x9b,0x54,0x54,0xe8,0xc8,0xae,0x78,0x99,0xa0,0xa3,0xf6,0x5d,0x78,0xe3,0x9e,0x5c,0xa7,0x18,0xb9,0x13,0x0c,0x04,0x9b,0xf3,0x7f,0x27,0x18,0xb0,0x20,0x8d,
- 0x04,0x20,0xf8,0x8d,0x64,0xd2,0xc8,0xe9,0x0f,0x51,0x03,0x1c,0x98,0x33,0x8f,0xe0,0x1e,0xe7,0xb6,0x16,0x8d,0x2a,0xf5,0xf3,0x19,0xce,0xdd,0x9e,0xee,0x17,0xc3,0x8f,0xd6,0xa1,0x20,0x8d,
- 0x04,0x20,0xfb,0xf1,0x17,0xd6,0x03,0x3b,0x01,0x8b,0x98,0xcf,0x16,0x20,0xde,0xaf,0x6c,0xed,0x60,0xab,0x6e,0x14,0x0b,0x58,0x6b,0x2d,0xf8,0x06,0x98,0x37,0x7a,0xff,0x7a,0x0f,0x20,0x8d,
0x04,0x20,0x0f,0xb9,0x71,0x05,0x64,0x83,0x2c,0x68,0x6a,0x9c,0xf0,0x4f,0xc3,0x90,0xcd,0x5c,0x73,0x9a,0xdd,0xb3,0xc6,0x42,0xca,0x09,0xbb,0xcc,0xfe,0x29,0x49,0x9f,0xc7,0x28,0x20,0x8d,
- 0x04,0x20,0x22,0x6e,0x42,0xe4,0xbd,0x2b,0xe5,0x3e,0x30,0xda,0x8a,0x03,0xf3,0x45,0x52,0xac,0x84,0xbf,0xbf,0xc5,0xaa,0x5f,0xe0,0x1b,0x26,0x28,0xb5,0x83,0x2e,0xed,0x4c,0xee,0x20,0x8d,
0x04,0x20,0x2a,0x47,0x8b,0xa0,0x4f,0x67,0x1d,0xcd,0x5d,0x84,0x1a,0xec,0xbd,0xd2,0xaa,0xe9,0x99,0x01,0x96,0x5d,0x4e,0xff,0x64,0x47,0xba,0xde,0xbf,0x56,0x89,0x39,0xac,0xde,0x20,0x8d,
0x04,0x20,0x2b,0xf3,0xe8,0xf5,0xef,0x90,0x14,0xab,0x61,0xe9,0x11,0x97,0x9f,0x18,0x4d,0xb4,0xff,0x89,0x94,0xf7,0x92,0x94,0x53,0xe6,0x9e,0xd4,0xdb,0x85,0x89,0x4d,0x3e,0xc9,0x20,0x8d,
- 0x04,0x20,0x2e,0x4e,0xde,0x51,0xd7,0x28,0x4b,0x29,0x7c,0xff,0x1f,0x8a,0x50,0xb7,0x5e,0xf0,0x81,0xcd,0xe8,0x8a,0x08,0x73,0x58,0x4e,0x43,0x1f,0x7b,0x85,0x9a,0xed,0xe2,0x68,0x20,0x8d,
0x04,0x20,0x35,0xdd,0xd0,0x36,0xa5,0x69,0x4a,0xd2,0xcc,0xb8,0xe9,0x62,0xa3,0x55,0xeb,0x86,0xe2,0xf3,0x03,0x48,0x26,0xe6,0x20,0xad,0xda,0xaa,0xff,0xde,0x16,0xad,0x39,0x9d,0x20,0x8d,
0x04,0x20,0x41,0x47,0x4e,0xc2,0xa1,0x71,0x63,0x3e,0x11,0x54,0x46,0x91,0x80,0xed,0x41,0x16,0x32,0x29,0x19,0x60,0xc9,0xef,0xa3,0xb7,0x96,0x2c,0x94,0xa8,0xdf,0x55,0xd7,0x21,0x20,0x8d,
0x04,0x20,0x44,0xf3,0xb7,0x5e,0x48,0x3c,0xbd,0xa6,0x52,0xaa,0x68,0xb5,0xbf,0xdc,0x01,0x5f,0x4b,0xeb,0x7a,0x25,0xcb,0x4a,0x70,0xbc,0x18,0x8c,0x97,0x5d,0x27,0x54,0x09,0x17,0x20,0x8d,
- 0x04,0x20,0x5c,0x52,0x7f,0x17,0x16,0x4c,0x27,0x36,0x2d,0x05,0xa1,0x19,0x0d,0xbe,0x87,0xab,0x24,0x7b,0xe7,0x38,0x3b,0xa1,0x7f,0xd1,0xd4,0x28,0x16,0x8e,0xfc,0x98,0x7d,0x08,0x20,0x8d,
+ 0x04,0x20,0x53,0xcd,0x56,0x48,0x48,0x8c,0x47,0x07,0x91,0x41,0x82,0x65,0x5b,0x76,0x64,0x03,0x4e,0x09,0xe6,0x6f,0x7e,0x8c,0xbf,0x10,0x84,0xe6,0x54,0xeb,0x56,0xc5,0xbd,0x88,0x20,0x8d,
0x04,0x20,0x67,0xc4,0x17,0xa5,0xcb,0x77,0xbd,0xaa,0x11,0x7f,0x8b,0xc0,0x81,0xf3,0xc0,0x96,0x9d,0x31,0x27,0x9c,0xad,0x6c,0x6d,0x98,0x42,0x70,0xdb,0x50,0x12,0x96,0x0b,0x36,0x20,0x8d,
- 0x04,0x20,0x73,0xdb,0x82,0xe0,0x88,0x40,0x49,0xd8,0x3b,0xa0,0xdd,0x83,0x7c,0x84,0x3c,0xb8,0xd0,0x03,0x0b,0x7a,0x08,0x44,0x4e,0x79,0xd6,0x61,0x23,0x31,0xa9,0xb3,0x07,0x58,0x20,0x8d,
- 0x04,0x20,0x75,0x93,0x21,0xdd,0x99,0x58,0x3c,0x3f,0xae,0x36,0x50,0x58,0x49,0xe2,0xd0,0xc3,0x3a,0x2c,0x4a,0xcf,0x41,0xc4,0x82,0x48,0xab,0xec,0x07,0x5d,0x56,0x2c,0xb4,0x8d,0x20,0x8d,
- 0x04,0x20,0x87,0xd4,0x66,0x0f,0xed,0xf9,0xf5,0xf1,0xcb,0x85,0x37,0xec,0xe1,0x19,0xa8,0xa4,0x03,0xb7,0x13,0x59,0xbb,0xf8,0xd2,0x93,0x92,0x50,0xfa,0x30,0x7a,0xd8,0x43,0xd0,0x20,0x8d,
+ 0x04,0x20,0x65,0x98,0x55,0xd0,0x8a,0xe0,0x29,0xe6,0x5e,0xef,0xb7,0x8b,0x8f,0xc9,0x27,0x53,0x3d,0xd0,0x8c,0xa2,0xfa,0x32,0x2f,0xad,0xf9,0xdc,0xe2,0x4b,0x14,0x66,0x3e,0x23,0x20,0x8d,
0x04,0x20,0x8b,0xfe,0xad,0x19,0xdb,0x97,0x57,0x84,0xec,0xad,0x4f,0xb2,0xdf,0x69,0x53,0x04,0x57,0x19,0x16,0x7a,0x71,0xd7,0x2b,0xab,0x03,0xfd,0x76,0x4d,0xa0,0x70,0xc3,0xe7,0x20,0x8d,
- 0x04,0x20,0x96,0x25,0xde,0x4a,0xbc,0xbd,0x76,0x76,0xee,0x43,0x45,0x76,0xe0,0x0d,0x99,0x83,0xcd,0x83,0x8f,0x94,0xe5,0xde,0x7a,0xf2,0xf0,0x57,0xb8,0x25,0x54,0x17,0xcb,0x3b,0x20,0x8d,
- 0x04,0x20,0x98,0xc6,0x44,0x27,0x90,0x41,0xa6,0x98,0xf9,0x25,0x6c,0x59,0x0f,0x06,0x6d,0x44,0x59,0x0e,0xb2,0x46,0xb0,0xa4,0x37,0x88,0x69,0x8f,0xc1,0x32,0xcd,0x9f,0x15,0xd7,0x20,0x8d,
- 0x04,0x20,0xaa,0x3a,0x16,0x86,0xea,0x59,0x09,0x04,0x78,0xe5,0x10,0x92,0xe1,0x1d,0xad,0xf7,0x56,0x2b,0xac,0xb0,0x97,0x29,0x63,0x30,0xf4,0x1b,0xcf,0xde,0xf3,0x28,0x0a,0x29,0x20,0x8d,
- 0x04,0x20,0xbc,0x27,0xae,0x89,0xc1,0x67,0x73,0x0a,0x08,0x02,0xdf,0xb7,0xcc,0x94,0xc7,0x9f,0xf4,0x72,0x7a,0x9b,0x20,0x0c,0x5c,0x11,0x3d,0x22,0xd6,0x13,0x88,0x66,0x74,0xbf,0x20,0x8d,
+ 0x05,0x20,0xd7,0x7a,0x53,0x89,0xfe,0x02,0x6a,0x59,0xb7,0x0e,0xf8,0x6d,0x9d,0x81,0xbb,0x9f,0x01,0xc0,0xc4,0xee,0x7b,0x36,0x10,0x33,0x07,0xd2,0x29,0xd8,0xec,0xcd,0x8e,0xa3,0x00,0x00,
+ 0x05,0x20,0xd7,0xf1,0x19,0x9e,0x7d,0x0f,0x43,0x97,0x33,0x56,0xe8,0x12,0x1d,0x7d,0xa0,0x4d,0x21,0x5a,0x60,0x73,0xc8,0x7e,0x10,0x55,0x60,0x56,0xbb,0x65,0x50,0xa4,0x17,0x59,0x00,0x00,
+ 0x05,0x20,0xd1,0x17,0xe2,0x34,0x4a,0x61,0x70,0x8b,0x04,0xa2,0xb4,0xb0,0x65,0x59,0x52,0x75,0x67,0x86,0xe6,0x48,0x33,0x31,0x5d,0x87,0x38,0x57,0xd3,0xf8,0x40,0x07,0x73,0xa8,0x00,0x00,
+ 0x05,0x20,0xd9,0x9c,0x20,0xfe,0xc2,0xe6,0x6a,0x16,0x30,0x81,0x54,0xc9,0x3f,0x9a,0x89,0x10,0xa9,0x4b,0xf1,0x05,0x56,0xd5,0x04,0x2d,0xb7,0x6a,0x7b,0x67,0x8d,0xf0,0xbe,0x8f,0x00,0x00,
+ 0x05,0x20,0xdc,0xdb,0x2d,0x39,0xd5,0xe4,0xda,0xb5,0xb6,0x6e,0x98,0x33,0x8f,0x51,0x99,0x54,0x38,0x53,0xa8,0x4e,0xda,0x29,0xd8,0xb5,0x57,0x9d,0x36,0xf0,0x97,0x37,0x18,0x3f,0x00,0x00,
+ 0x05,0x20,0xe1,0x44,0x2d,0x6e,0xd3,0xd9,0xf0,0x95,0x6c,0x52,0x2e,0x44,0x3c,0x27,0x3d,0x78,0xac,0x6e,0x8f,0x27,0x1c,0x0c,0xc0,0x78,0x22,0x3e,0xa1,0x84,0x01,0x42,0x08,0x5c,0x00,0x00,
+ 0x05,0x20,0xe3,0xa5,0x88,0x11,0x4d,0x3d,0xfb,0x02,0xec,0x1f,0xda,0x48,0x86,0x12,0xf6,0x12,0xd9,0x3e,0x68,0x49,0xa7,0xae,0x37,0xfd,0x02,0x48,0x38,0x8b,0xdc,0xd4,0xa6,0x8f,0x00,0x00,
+ 0x05,0x20,0xe5,0x19,0x24,0x71,0xab,0x61,0xb0,0xfe,0x44,0x4a,0x74,0x8d,0xca,0x90,0xc3,0xd6,0x24,0xb4,0xd5,0x03,0xe7,0xf3,0x4f,0xbe,0x12,0x72,0xd6,0xa0,0x4b,0x22,0x0b,0xe1,0x00,0x00,
+ 0x05,0x20,0xee,0xab,0xea,0x3b,0xc2,0x8a,0xe2,0xb3,0xe5,0xe7,0x92,0xf0,0x88,0x05,0x68,0x8d,0x2f,0xe7,0x2c,0xd2,0x00,0x39,0xd4,0x5d,0x07,0xfc,0xa1,0xd6,0xbf,0x89,0xa2,0x40,0x00,0x00,
+ 0x05,0x20,0xf2,0x74,0x4c,0x90,0xc3,0xd9,0x34,0x4d,0x5f,0x6e,0xdb,0xdd,0x7d,0xef,0xa5,0xed,0x6e,0x59,0x9e,0x31,0x41,0x94,0x38,0x84,0xc5,0x08,0xd2,0x23,0xb3,0xa7,0xe0,0x2c,0x00,0x00,
+ 0x05,0x20,0xf3,0x77,0xe5,0xa7,0x11,0xef,0x65,0x91,0x23,0xb8,0x32,0x06,0xcb,0xc0,0x91,0xf7,0x21,0x1d,0x70,0xbc,0x83,0x1b,0x86,0x34,0x35,0x31,0x0f,0x9f,0xc1,0x0d,0xbb,0x56,0x00,0x00,
+ 0x05,0x20,0xfe,0xb0,0x99,0x79,0x95,0x58,0x71,0xb5,0x63,0xcc,0x33,0xeb,0x55,0x91,0x8c,0xb4,0x3a,0xf2,0x8b,0x2d,0x8e,0x47,0xbe,0x25,0x47,0x12,0xcd,0x14,0x48,0xf0,0x1d,0xea,0x00,0x00,
+ 0x05,0x20,0xfc,0x79,0x14,0x77,0x6b,0x0e,0x34,0x8d,0xde,0x01,0x33,0xed,0xb4,0x0e,0xa7,0xc9,0x15,0x4f,0xd0,0x27,0x2c,0xd6,0x5f,0xe9,0x63,0x82,0x8d,0xd5,0x0c,0x9e,0x18,0x29,0x00,0x00,
0x05,0x20,0x07,0x61,0x26,0xd7,0x6c,0x05,0xbf,0xf6,0x2d,0x8c,0xca,0xc4,0x65,0xd3,0xd3,0xb2,0x49,0xe9,0xcc,0x53,0x1e,0xca,0x77,0x84,0xb6,0x10,0x5e,0xc2,0x5a,0xfe,0x28,0xb3,0x00,0x00,
- 0x05,0x20,0x0a,0x26,0x27,0x45,0xb1,0x1e,0xfc,0x27,0x03,0x32,0x0e,0x65,0x9e,0x3c,0x64,0x0e,0x33,0x50,0x3d,0x6c,0x90,0x17,0x0e,0x29,0xee,0x5a,0x58,0xdf,0x08,0xde,0xbf,0x73,0x00,0x00,
+ 0x05,0x20,0x03,0xaa,0x47,0xe9,0xe2,0x77,0xeb,0xa5,0x72,0x27,0x23,0x8b,0x13,0x62,0x61,0x32,0xb5,0xb2,0x1b,0x5a,0x18,0xb2,0xf9,0x26,0x06,0x84,0xee,0x28,0x42,0xac,0xba,0xbc,0x00,0x00,
+ 0x05,0x20,0x08,0xc6,0x19,0x31,0x40,0x96,0xf3,0xe2,0x81,0x4e,0x88,0x54,0x54,0x9e,0xbf,0xfa,0x38,0x7a,0xfa,0x38,0x96,0x13,0x2a,0xc4,0x69,0xa2,0xae,0xe5,0x94,0xc7,0x16,0xb7,0x00,0x00,
+ 0x05,0x20,0x0a,0x26,0x27,0x23,0xdd,0xf3,0x56,0xbe,0x9e,0x9e,0xa7,0xc6,0x3c,0xc5,0x99,0xc4,0x87,0x3b,0x4d,0xb9,0x13,0x62,0x91,0xf2,0x25,0x1c,0x02,0x42,0x63,0xe3,0x63,0x7a,0x00,0x00,
+ 0x05,0x20,0x0c,0x50,0x55,0x46,0x87,0x5a,0x8d,0x14,0xfb,0xa7,0x29,0x70,0x18,0xa6,0x29,0x80,0x8c,0x33,0x42,0x5a,0x8f,0xe4,0x84,0x64,0x3d,0x0e,0xb5,0xbd,0x36,0x34,0x42,0xb6,0x00,0x00,
0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x00,0x00,
- 0x05,0x20,0x19,0xe7,0x0d,0x3f,0xfe,0x9e,0x0e,0x8e,0x73,0x40,0x40,0xc3,0xba,0x8f,0x41,0xaf,0xf1,0x7b,0xa6,0x83,0x1b,0xc3,0xa4,0xe0,0x6d,0x6c,0x57,0xa7,0x36,0x5d,0x09,0xce,0x00,0x00,
+ 0x05,0x20,0x18,0x31,0xb3,0x9a,0xf8,0x8c,0xec,0x99,0x2e,0x7d,0xe4,0x90,0xa2,0x54,0x27,0xbd,0xe5,0xc8,0x65,0xdf,0x1f,0xaa,0x8f,0xe9,0x0f,0x64,0x85,0x09,0xc3,0x70,0x62,0x13,0x00,0x00,
+ 0x05,0x20,0x1a,0x35,0x98,0x78,0xb1,0xd9,0x48,0x62,0xe9,0x23,0x10,0xfe,0x71,0xdb,0x10,0x5f,0x28,0xc8,0xf3,0xda,0x33,0x9b,0x26,0xcb,0x4d,0xbe,0x7b,0x03,0x76,0xb9,0xe0,0x54,0x00,0x00,
+ 0x05,0x20,0x27,0x7a,0xaf,0x5a,0x9c,0xf4,0x72,0xfe,0x3c,0xdd,0x7a,0xba,0xd7,0x98,0x31,0xde,0x73,0xce,0x84,0x5b,0x41,0xe7,0x9a,0x6a,0xe2,0xc1,0x3b,0x5b,0x37,0x23,0xc7,0xdf,0x00,0x00,
+ 0x05,0x20,0x20,0x90,0xe3,0xd3,0xad,0x87,0xeb,0x2a,0xd9,0x29,0x17,0x74,0x47,0xc9,0x54,0x57,0xfa,0x3d,0x71,0x02,0x11,0xb2,0xc3,0x87,0x31,0xb3,0x9b,0x6f,0x2e,0xfc,0x30,0xea,0x00,0x00,
+ 0x05,0x20,0x22,0x56,0xd6,0x98,0x11,0x61,0xe1,0x5a,0x34,0x9f,0xe2,0x9d,0xf5,0x2b,0xbd,0xbc,0xcc,0x1c,0xf5,0x1d,0x68,0xa5,0xca,0xb1,0xb5,0x4b,0xf1,0xb5,0xff,0x1e,0xdd,0xc0,0x00,0x00,
+ 0x05,0x20,0x37,0x3e,0x28,0x39,0xef,0xa6,0xbc,0xf8,0xf1,0xba,0x11,0x40,0x87,0x42,0xff,0x58,0x46,0x8f,0xb0,0x80,0xfe,0x20,0x75,0xc5,0x43,0xce,0xec,0x9b,0x5d,0x37,0x19,0xf4,0x00,0x00,
0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x00,0x00,
- 0x05,0x20,0x3b,0x42,0x1c,0x25,0xf7,0xbf,0x79,0xed,0x6d,0x7d,0xef,0x65,0x30,0x7d,0xee,0x16,0x37,0x22,0x72,0x43,0x33,0x28,0x40,0xa3,0xaa,0xf4,0x48,0x49,0x67,0xb1,0x4b,0xfd,0x00,0x00,
+ 0x05,0x20,0x39,0xca,0x8e,0x62,0x0a,0x36,0xa7,0x68,0x22,0xc4,0xcc,0x4a,0xa9,0x16,0x69,0x4b,0x8a,0x1c,0x5f,0x6e,0x4a,0x98,0xb6,0x95,0x82,0xb3,0x66,0x66,0xc5,0x29,0x3a,0xb0,0x00,0x00,
+ 0x05,0x20,0x3b,0xd0,0x80,0xc4,0xab,0x82,0x83,0x11,0x48,0xe5,0x3b,0x23,0x3b,0x17,0x04,0x0f,0xd8,0x39,0x7c,0x1b,0x70,0x73,0x68,0x98,0x11,0x22,0x49,0x51,0x8f,0x20,0x65,0x65,0x00,0x00,
+ 0x05,0x20,0x47,0x1f,0x83,0xc8,0xa3,0x87,0x35,0xcf,0x8e,0x8d,0x22,0xfe,0xf0,0x02,0x63,0x04,0x3a,0x6a,0x49,0x1a,0x3b,0x70,0x17,0xeb,0x05,0xed,0xaf,0x61,0xb6,0x26,0xb8,0x21,0x00,0x00,
+ 0x05,0x20,0x45,0xbd,0x33,0x3d,0x81,0x1e,0x14,0x52,0x88,0x8a,0xa7,0x46,0x0f,0x37,0x25,0xd0,0x59,0x9f,0x78,0xb5,0x7f,0x4a,0xf1,0x54,0x8c,0x2d,0x36,0x32,0xf8,0x10,0xf3,0xcc,0x00,0x00,
+ 0x05,0x20,0x4a,0x8b,0x40,0x25,0xdc,0x06,0x2a,0xed,0x44,0x35,0xec,0x06,0x9e,0x73,0x70,0xf0,0x07,0x06,0x35,0xd1,0x60,0x4f,0x22,0xe8,0xbf,0x8a,0xdf,0xd9,0xeb,0x97,0x73,0x06,0x00,0x00,
0x05,0x20,0x4e,0x77,0x2e,0x12,0x91,0x67,0x6b,0x94,0xc4,0x92,0x2f,0x19,0x67,0x7d,0xcd,0x47,0x02,0xad,0xf8,0x60,0x72,0xed,0x73,0xf1,0x10,0x99,0x2c,0x05,0x61,0x66,0x55,0xd9,0x00,0x00,
- 0x05,0x20,0x53,0x94,0xa6,0x3e,0x14,0x82,0xd4,0xf9,0xd3,0xa7,0x53,0x33,0x05,0xce,0x72,0x64,0xed,0x74,0x09,0x63,0x8f,0x24,0xef,0xda,0x12,0xa1,0x55,0xe0,0xd8,0xbb,0xd3,0x58,0x00,0x00,
+ 0x05,0x20,0x5a,0x29,0xfe,0x8a,0xaa,0x9d,0x78,0x81,0x04,0x53,0x37,0xf5,0x6f,0xb6,0xe1,0x57,0x08,0x80,0xcf,0xf6,0x03,0x11,0x92,0x8d,0x08,0xe3,0x99,0x9f,0x98,0x4a,0x27,0x6b,0x00,0x00,
+ 0x05,0x20,0x5a,0x65,0x0a,0x52,0x9b,0xc7,0x2e,0x92,0x79,0x7b,0xd3,0xf7,0xa4,0x35,0xbe,0x8e,0xb1,0xea,0x2d,0x7e,0x73,0x84,0x6a,0x3f,0xc9,0x9a,0x62,0xe3,0xc6,0x17,0x0e,0x7c,0x00,0x00,
+ 0x05,0x20,0x5c,0x40,0x7f,0x80,0x43,0x91,0x9c,0xfc,0x04,0xdc,0xdc,0x8e,0x01,0xda,0xc8,0xaf,0x90,0x62,0x64,0x16,0xf7,0x11,0xe4,0x87,0xac,0xa4,0x06,0x6f,0x8d,0x87,0x4e,0xd6,0x00,0x00,
+ 0x05,0x20,0x5e,0x69,0x4f,0x31,0x33,0xa7,0xea,0x3e,0xf4,0x7a,0x0a,0x1e,0x74,0x09,0x07,0xa1,0x50,0xf7,0x03,0xf5,0xc6,0x19,0xeb,0x95,0xaa,0x63,0x17,0x10,0x6e,0x68,0xca,0x10,0x00,0x00,
+ 0x05,0x20,0x67,0x82,0xfc,0x36,0xea,0xae,0x95,0x3b,0x5d,0x46,0xf3,0xf4,0x6c,0x50,0x69,0x29,0xc7,0x47,0x87,0xca,0xa6,0x40,0x12,0x40,0x6d,0x12,0x94,0x35,0x17,0x8a,0xba,0x56,0x00,0x00,
+ 0x05,0x20,0x67,0xab,0xce,0xf2,0xe3,0xf3,0xf6,0x19,0xf6,0x56,0xa2,0x4c,0xca,0x91,0x4b,0x93,0xfe,0xbc,0x85,0x50,0x5c,0x20,0x51,0x69,0xfb,0xf0,0x92,0xbb,0x57,0xa3,0x0d,0x05,0x00,0x00,
+ 0x05,0x20,0x62,0xcc,0x44,0x66,0x31,0x76,0x1b,0x43,0xbe,0xe1,0xc9,0x1d,0x20,0x26,0x7d,0xa7,0x06,0x31,0x57,0x0d,0xb9,0x58,0x20,0xb3,0xca,0xb2,0x5e,0x0a,0x15,0x0b,0xba,0x0d,0x00,0x00,
+ 0x05,0x20,0x71,0x68,0x1b,0xc7,0x48,0x8f,0xe9,0xa3,0x53,0x29,0xb6,0x23,0x5a,0x25,0x02,0x45,0x72,0xca,0xa1,0xb6,0x06,0xfc,0xda,0x65,0x71,0x37,0xe6,0xd9,0x30,0x81,0x02,0xfe,0x00,0x00,
+ 0x05,0x20,0x72,0x8b,0x72,0x38,0xfe,0x44,0xe9,0xc2,0xf4,0xbc,0xd8,0xce,0x1c,0xd5,0x50,0xb1,0x63,0x56,0x74,0x5e,0xf2,0xd3,0xe5,0xd5,0x29,0xe4,0x34,0x1d,0xf5,0x1c,0x7a,0xb9,0x00,0x00,
+ 0x05,0x20,0x76,0x74,0x80,0x6c,0xab,0x7b,0x36,0x3a,0x6a,0x78,0xa4,0xa8,0xb8,0xb7,0xe7,0xf9,0x34,0x47,0x6d,0x34,0xca,0xa2,0xc6,0xef,0x81,0xab,0x62,0xb1,0x46,0x86,0xaf,0xd0,0x00,0x00,
+ 0x05,0x20,0x80,0xfc,0x95,0xc8,0x95,0x91,0x2f,0x6b,0x6e,0xc4,0x2b,0xe1,0x2f,0xa0,0xcb,0xb8,0x76,0x5f,0x6f,0x1c,0x27,0xb0,0x3a,0x2c,0xb9,0x8d,0x6c,0x55,0xbd,0x02,0x60,0xe0,0x00,0x00,
+ 0x05,0x20,0x80,0xc6,0x6f,0xb3,0x18,0x5a,0x1a,0xde,0x4e,0xde,0x50,0xd2,0xc6,0x3f,0xc5,0x96,0x09,0x35,0x3a,0x4d,0x88,0x5f,0xa3,0x49,0x37,0xff,0xe6,0xc5,0x43,0x10,0xaf,0xa8,0x00,0x00,
+ 0x05,0x20,0x8a,0x32,0x54,0x37,0x12,0x24,0xb5,0x1d,0xba,0x3c,0x45,0x03,0x2e,0xda,0xfe,0xf1,0x87,0x8f,0x31,0xe5,0xfe,0xed,0xfa,0x38,0x25,0x00,0x99,0xa5,0xf4,0x51,0x02,0xf7,0x00,0x00,
+ 0x05,0x20,0x97,0x4e,0x74,0xcd,0x8b,0x37,0x76,0x15,0x3b,0x6d,0xb5,0xa4,0xa0,0x4b,0xf7,0x34,0x98,0x48,0x67,0x1e,0x99,0xe7,0x24,0xf4,0x00,0xf7,0x06,0x9c,0x45,0x69,0x34,0x01,0x00,0x00,
0x05,0x20,0x91,0x06,0xd1,0x9e,0xbd,0xab,0xc4,0x61,0xb3,0x0a,0xc2,0x3b,0x29,0xf3,0x10,0x38,0xee,0xbd,0x9d,0xe3,0x99,0x97,0x30,0x70,0x6e,0xe6,0xfb,0x6a,0x3c,0x07,0x3d,0xfd,0x00,0x00,
+ 0x05,0x20,0x9c,0x97,0xc1,0xad,0xf4,0xd2,0x07,0xae,0xe8,0x3e,0x14,0x42,0x36,0x85,0x71,0xa0,0xa7,0xef,0x72,0x44,0xf1,0x74,0x8b,0x6f,0xa5,0xa4,0x2c,0x0d,0xcc,0x9f,0xb0,0x9d,0x00,0x00,
+ 0x05,0x20,0x9d,0x0d,0x0f,0x58,0x1a,0x5c,0xb4,0x1a,0xeb,0xef,0x8e,0x91,0x8d,0x8c,0x1b,0x57,0x5d,0x6d,0x97,0x24,0x28,0x45,0x54,0x8a,0x3a,0xd5,0x05,0xfb,0x76,0xac,0x25,0x52,0x00,0x00,
+ 0x05,0x20,0xa4,0xb3,0x30,0x54,0x28,0x0f,0xfb,0xe5,0x76,0xb0,0x31,0xb2,0x65,0x62,0x56,0x72,0x7c,0xc9,0xcd,0x07,0x4d,0x5f,0xb1,0x69,0xe0,0xf7,0x35,0x3d,0x30,0x3c,0x7d,0x64,0x00,0x00,
+ 0x05,0x20,0xa9,0xa9,0xe5,0xae,0x01,0xc2,0x5e,0x76,0x2f,0x5d,0xa3,0x07,0xdc,0xce,0xb8,0xbc,0x6f,0x47,0xaf,0x3a,0x37,0xf8,0x5c,0x86,0xff,0xe9,0xb6,0xa5,0x00,0x93,0x76,0x11,0x00,0x00,
+ 0x05,0x20,0xb2,0x63,0x45,0xf5,0x36,0xb0,0x79,0x58,0x0d,0x8a,0x54,0x52,0x16,0x2f,0x1f,0x74,0x93,0xe0,0x30,0x82,0x1b,0xe4,0x01,0x76,0xf5,0x03,0xa1,0x19,0xa3,0x8d,0x0e,0xce,0x00,0x00,
+ 0x05,0x20,0xb5,0x55,0x31,0x3f,0xe7,0xc7,0x17,0xe4,0x31,0x87,0x47,0x45,0x7c,0x67,0x43,0x5c,0x82,0x73,0xd6,0x62,0x64,0x94,0x92,0x32,0x2d,0x81,0x0e,0x01,0x35,0xc0,0x7e,0xb7,0x00,0x00,
0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x00,0x00,
+ 0x05,0x20,0xba,0xe0,0xd1,0xe5,0x2e,0x27,0x5b,0x1d,0x36,0x57,0x77,0xaf,0x64,0x04,0xfc,0xe1,0x8f,0x8c,0xf1,0x25,0x81,0x2b,0x4f,0x6a,0xf8,0x55,0x48,0xc2,0x5e,0x6f,0x62,0x43,0x00,0x00,
+ 0x05,0x20,0xc0,0xb9,0x7b,0x21,0xbd,0xa2,0x48,0xda,0x8a,0x3e,0xc3,0x6c,0xac,0xfd,0x6d,0x63,0x21,0xb6,0xb3,0x37,0xa9,0x4d,0x42,0x2c,0x9e,0x75,0x61,0x07,0xdc,0xc9,0xab,0x9b,0x00,0x00,
+ 0x05,0x20,0xc8,0xdc,0x00,0xc8,0xdf,0xa1,0xb2,0xe9,0x9f,0x00,0xb3,0x86,0x93,0xc3,0xbc,0x6d,0x56,0xe2,0x83,0xfc,0xf4,0x6e,0x55,0x5d,0xed,0x4e,0x53,0xe6,0xd1,0x4c,0x38,0x3c,0x00,0x00,
0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x00,0x00,
+ 0x05,0x20,0xce,0x25,0x15,0xbd,0x08,0xe9,0x67,0x1c,0xa2,0xa5,0x16,0x0e,0x38,0xd9,0xe4,0xc6,0x20,0x31,0x86,0x23,0x21,0x5a,0x5a,0x76,0x1e,0x74,0xd5,0xd3,0x4e,0x86,0x61,0xf4,0x00,0x00,
0x06,0x10,0xfc,0x32,0x17,0xea,0xe4,0x15,0xc3,0xbf,0x98,0x08,0x14,0x9d,0xb5,0xa2,0xc9,0xaa,0x20,0x8d,
0x06,0x10,0xfc,0xc7,0xbe,0x49,0xcc,0xd1,0xdc,0x91,0x31,0x25,0xf0,0xda,0x45,0x7d,0x08,0xce,0x20,0x8d,
};
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 794e0f5383..7c35222713 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -8,6 +8,7 @@
#include <uint256.h>
+#include <chrono>
#include <limits>
#include <map>
@@ -109,6 +110,10 @@ struct Params {
bool fPowNoRetargeting;
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;
+ std::chrono::seconds PowTargetSpacing() const
+ {
+ return std::chrono::seconds{nPowTargetSpacing};
+ }
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
/** The best chain should have at least this much work */
uint256 nMinimumChainWork;
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index 6027bb9aeb..9c0aa09356 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -79,6 +79,7 @@ enum class BlockValidationResult {
BLOCK_INVALID_PREV, //!< A block this one builds on is invalid
BLOCK_TIME_FUTURE, //!< block timestamp was > 2 hours in the future (or our clock is bad)
BLOCK_CHECKPOINT, //!< the block failed to meet one of our checkpoints
+ BLOCK_HEADER_LOW_WORK //!< the block header may be on a too-little-work chain
};
diff --git a/src/headerssync.cpp b/src/headerssync.cpp
new file mode 100644
index 0000000000..3eca492d81
--- /dev/null
+++ b/src/headerssync.cpp
@@ -0,0 +1,317 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <headerssync.h>
+#include <logging.h>
+#include <pow.h>
+#include <timedata.h>
+#include <util/check.h>
+
+// The two constants below are computed using the simulation script on
+// https://gist.github.com/sipa/016ae445c132cdf65a2791534dfb7ae1
+
+//! Store a commitment to a header every HEADER_COMMITMENT_PERIOD blocks.
+constexpr size_t HEADER_COMMITMENT_PERIOD{584};
+
+//! Only feed headers to validation once this many headers on top have been
+//! received and validated against commitments.
+constexpr size_t REDOWNLOAD_BUFFER_SIZE{13959}; // 13959/584 = ~23.9 commitments
+
+// Our memory analysis assumes 48 bytes for a CompressedHeader (so we should
+// re-calculate parameters if we compress further)
+static_assert(sizeof(CompressedHeader) == 48);
+
+HeadersSyncState::HeadersSyncState(NodeId id, const Consensus::Params& consensus_params,
+ const CBlockIndex* chain_start, const arith_uint256& minimum_required_work) :
+ m_id(id), m_consensus_params(consensus_params),
+ m_chain_start(chain_start),
+ m_minimum_required_work(minimum_required_work),
+ m_current_chain_work(chain_start->nChainWork),
+ m_commit_offset(GetRand<unsigned>(HEADER_COMMITMENT_PERIOD)),
+ m_last_header_received(m_chain_start->GetBlockHeader()),
+ m_current_height(chain_start->nHeight)
+{
+ // Estimate the number of blocks that could possibly exist on the peer's
+ // chain *right now* using 6 blocks/second (fastest blockrate given the MTP
+ // rule) times the number of seconds from the last allowed block until
+ // today. This serves as a memory bound on how many commitments we might
+ // store from this peer, and we can safely give up syncing if the peer
+ // exceeds this bound, because it's not possible for a consensus-valid
+ // chain to be longer than this (at the current time -- in the future we
+ // could try again, if necessary, to sync a longer chain).
+ m_max_commitments = 6*(Ticks<std::chrono::seconds>(GetAdjustedTime() - NodeSeconds{std::chrono::seconds{chain_start->GetMedianTimePast()}}) + MAX_FUTURE_BLOCK_TIME) / HEADER_COMMITMENT_PERIOD;
+
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync started with peer=%d: height=%i, max_commitments=%i, min_work=%s\n", m_id, m_current_height, m_max_commitments, m_minimum_required_work.ToString());
+}
+
+/** Free any memory in use, and mark this object as no longer usable. This is
+ * required to guarantee that we won't reuse this object with the same
+ * SaltedTxidHasher for another sync. */
+void HeadersSyncState::Finalize()
+{
+ Assume(m_download_state != State::FINAL);
+ m_header_commitments = {};
+ m_last_header_received.SetNull();
+ m_redownloaded_headers = {};
+ m_redownload_buffer_last_hash.SetNull();
+ m_redownload_buffer_first_prev_hash.SetNull();
+ m_process_all_remaining_headers = false;
+ m_current_height = 0;
+
+ m_download_state = State::FINAL;
+}
+
+/** Process the next batch of headers received from our peer.
+ * Validate and store commitments, and compare total chainwork to our target to
+ * see if we can switch to REDOWNLOAD mode. */
+HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
+ std::vector<CBlockHeader>& received_headers, const bool full_headers_message)
+{
+ ProcessingResult ret;
+
+ Assume(!received_headers.empty());
+ if (received_headers.empty()) return ret;
+
+ Assume(m_download_state != State::FINAL);
+ if (m_download_state == State::FINAL) return ret;
+
+ if (m_download_state == State::PRESYNC) {
+ // During PRESYNC, we minimally validate block headers and
+ // occasionally add commitments to them, until we reach our work
+ // threshold (at which point m_download_state is updated to REDOWNLOAD).
+ ret.success = ValidateAndStoreHeadersCommitments(received_headers);
+ if (ret.success) {
+ if (full_headers_message || m_download_state == State::REDOWNLOAD) {
+ // A full headers message means the peer may have more to give us;
+ // also if we just switched to REDOWNLOAD then we need to re-request
+ // headers from the beginning.
+ ret.request_more = true;
+ } else {
+ Assume(m_download_state == State::PRESYNC);
+ // If we're in PRESYNC and we get a non-full headers
+ // message, then the peer's chain has ended and definitely doesn't
+ // have enough work, so we can stop our sync.
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: incomplete headers message at height=%i (presync phase)\n", m_id, m_current_height);
+ }
+ }
+ } else if (m_download_state == State::REDOWNLOAD) {
+ // During REDOWNLOAD, we compare our stored commitments to what we
+ // receive, and add headers to our redownload buffer. When the buffer
+ // gets big enough (meaning that we've checked enough commitments),
+ // we'll return a batch of headers to the caller for processing.
+ ret.success = true;
+ for (const auto& hdr : received_headers) {
+ if (!ValidateAndStoreRedownloadedHeader(hdr)) {
+ // Something went wrong -- the peer gave us an unexpected chain.
+ // We could consider looking at the reason for failure and
+ // punishing the peer, but for now just give up on sync.
+ ret.success = false;
+ break;
+ }
+ }
+
+ if (ret.success) {
+ // Return any headers that are ready for acceptance.
+ ret.pow_validated_headers = PopHeadersReadyForAcceptance();
+
+ // If we hit our target blockhash, then all remaining headers will be
+ // returned and we can clear any leftover internal state.
+ if (m_redownloaded_headers.empty() && m_process_all_remaining_headers) {
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync complete with peer=%d: releasing all at height=%i (redownload phase)\n", m_id, m_redownload_buffer_last_height);
+ } else if (full_headers_message) {
+ // If the headers message is full, we need to request more.
+ ret.request_more = true;
+ } else {
+ // For some reason our peer gave us a high-work chain, but is now
+ // declining to serve us that full chain again. Give up.
+ // Note that there's no more processing to be done with these
+ // headers, so we can still return success.
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: incomplete headers message at height=%i (redownload phase)\n", m_id, m_redownload_buffer_last_height);
+ }
+ }
+ }
+
+ if (!(ret.success && ret.request_more)) Finalize();
+ return ret;
+}
+
+bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlockHeader>& headers)
+{
+ // The caller should not give us an empty set of headers.
+ Assume(headers.size() > 0);
+ if (headers.size() == 0) return true;
+
+ Assume(m_download_state == State::PRESYNC);
+ if (m_download_state != State::PRESYNC) return false;
+
+ if (headers[0].hashPrevBlock != m_last_header_received.GetHash()) {
+ // Somehow our peer gave us a header that doesn't connect.
+ // This might be benign -- perhaps our peer reorged away from the chain
+ // they were on. Give up on this sync for now (likely we will start a
+ // new sync with a new starting point).
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: non-continuous headers at height=%i (presync phase)\n", m_id, m_current_height);
+ return false;
+ }
+
+ // If it does connect, (minimally) validate and occasionally store
+ // commitments.
+ for (const auto& hdr : headers) {
+ if (!ValidateAndProcessSingleHeader(hdr)) {
+ return false;
+ }
+ }
+
+ if (m_current_chain_work >= m_minimum_required_work) {
+ m_redownloaded_headers.clear();
+ m_redownload_buffer_last_height = m_chain_start->nHeight;
+ m_redownload_buffer_first_prev_hash = m_chain_start->GetBlockHash();
+ m_redownload_buffer_last_hash = m_chain_start->GetBlockHash();
+ m_redownload_chain_work = m_chain_start->nChainWork;
+ m_download_state = State::REDOWNLOAD;
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync transition with peer=%d: reached sufficient work at height=%i, redownloading from height=%i\n", m_id, m_current_height, m_redownload_buffer_last_height);
+ }
+ return true;
+}
+
+bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& current)
+{
+ Assume(m_download_state == State::PRESYNC);
+ if (m_download_state != State::PRESYNC) return false;
+
+ int next_height = m_current_height + 1;
+
+ // Verify that the difficulty isn't growing too fast; an adversary with
+ // limited hashing capability has a greater chance of producing a high
+ // work chain if they compress the work into as few blocks as possible,
+ // so don't let anyone give a chain that would violate the difficulty
+ // adjustment maximum.
+ if (!PermittedDifficultyTransition(m_consensus_params, next_height,
+ m_last_header_received.nBits, current.nBits)) {
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: invalid difficulty transition at height=%i (presync phase)\n", m_id, next_height);
+ return false;
+ }
+
+ if (next_height % HEADER_COMMITMENT_PERIOD == m_commit_offset) {
+ // Add a commitment.
+ m_header_commitments.push_back(m_hasher(current.GetHash()) & 1);
+ if (m_header_commitments.size() > m_max_commitments) {
+ // The peer's chain is too long; give up.
+ // It's possible the chain grew since we started the sync; so
+ // potentially we could succeed in syncing the peer's chain if we
+ // try again later.
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: exceeded max commitments at height=%i (presync phase)\n", m_id, next_height);
+ return false;
+ }
+ }
+
+ m_current_chain_work += GetBlockProof(CBlockIndex(current));
+ m_last_header_received = current;
+ m_current_height = next_height;
+
+ return true;
+}
+
+bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& header)
+{
+ Assume(m_download_state == State::REDOWNLOAD);
+ if (m_download_state != State::REDOWNLOAD) return false;
+
+ int64_t next_height = m_redownload_buffer_last_height + 1;
+
+ // Ensure that we're working on a header that connects to the chain we're
+ // downloading.
+ if (header.hashPrevBlock != m_redownload_buffer_last_hash) {
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: non-continuous headers at height=%i (redownload phase)\n", m_id, next_height);
+ return false;
+ }
+
+ // Check that the difficulty adjustments are within our tolerance:
+ uint32_t previous_nBits{0};
+ if (!m_redownloaded_headers.empty()) {
+ previous_nBits = m_redownloaded_headers.back().nBits;
+ } else {
+ previous_nBits = m_chain_start->nBits;
+ }
+
+ if (!PermittedDifficultyTransition(m_consensus_params, next_height,
+ previous_nBits, header.nBits)) {
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: invalid difficulty transition at height=%i (redownload phase)\n", m_id, next_height);
+ return false;
+ }
+
+ // Track work on the redownloaded chain
+ m_redownload_chain_work += GetBlockProof(CBlockIndex(header));
+
+ if (m_redownload_chain_work >= m_minimum_required_work) {
+ m_process_all_remaining_headers = true;
+ }
+
+ // If we're at a header for which we previously stored a commitment, verify
+ // it is correct. Failure will result in aborting download.
+ // Also, don't check commitments once we've gotten to our target blockhash;
+ // it's possible our peer has extended its chain between our first sync and
+ // our second, and we don't want to return failure after we've seen our
+ // target blockhash just because we ran out of commitments.
+ if (!m_process_all_remaining_headers && next_height % HEADER_COMMITMENT_PERIOD == m_commit_offset) {
+ if (m_header_commitments.size() == 0) {
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: commitment overrun at height=%i (redownload phase)\n", m_id, next_height);
+ // Somehow our peer managed to feed us a different chain and
+ // we've run out of commitments.
+ return false;
+ }
+ bool commitment = m_hasher(header.GetHash()) & 1;
+ bool expected_commitment = m_header_commitments.front();
+ m_header_commitments.pop_front();
+ if (commitment != expected_commitment) {
+ LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: commitment mismatch at height=%i (redownload phase)\n", m_id, next_height);
+ return false;
+ }
+ }
+
+ // Store this header for later processing.
+ m_redownloaded_headers.push_back(header);
+ m_redownload_buffer_last_height = next_height;
+ m_redownload_buffer_last_hash = header.GetHash();
+
+ return true;
+}
+
+std::vector<CBlockHeader> HeadersSyncState::PopHeadersReadyForAcceptance()
+{
+ std::vector<CBlockHeader> ret;
+
+ Assume(m_download_state == State::REDOWNLOAD);
+ if (m_download_state != State::REDOWNLOAD) return ret;
+
+ while (m_redownloaded_headers.size() > REDOWNLOAD_BUFFER_SIZE ||
+ (m_redownloaded_headers.size() > 0 && m_process_all_remaining_headers)) {
+ ret.emplace_back(m_redownloaded_headers.front().GetFullHeader(m_redownload_buffer_first_prev_hash));
+ m_redownloaded_headers.pop_front();
+ m_redownload_buffer_first_prev_hash = ret.back().GetHash();
+ }
+ return ret;
+}
+
+CBlockLocator HeadersSyncState::NextHeadersRequestLocator() const
+{
+ Assume(m_download_state != State::FINAL);
+ if (m_download_state == State::FINAL) return {};
+
+ auto chain_start_locator = LocatorEntries(m_chain_start);
+ std::vector<uint256> locator;
+
+ if (m_download_state == State::PRESYNC) {
+ // During pre-synchronization, we continue from the last header received.
+ locator.push_back(m_last_header_received.GetHash());
+ }
+
+ if (m_download_state == State::REDOWNLOAD) {
+ // During redownload, we will download from the last received header that we stored.
+ locator.push_back(m_redownload_buffer_last_hash);
+ }
+
+ locator.insert(locator.end(), chain_start_locator.begin(), chain_start_locator.end());
+
+ return CBlockLocator{std::move(locator)};
+}
diff --git a/src/headerssync.h b/src/headerssync.h
new file mode 100644
index 0000000000..16da964246
--- /dev/null
+++ b/src/headerssync.h
@@ -0,0 +1,277 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_HEADERSSYNC_H
+#define BITCOIN_HEADERSSYNC_H
+
+#include <arith_uint256.h>
+#include <chain.h>
+#include <consensus/params.h>
+#include <net.h> // For NodeId
+#include <primitives/block.h>
+#include <uint256.h>
+#include <util/bitdeque.h>
+#include <util/hasher.h>
+
+#include <deque>
+#include <vector>
+
+// A compressed CBlockHeader, which leaves out the prevhash
+struct CompressedHeader {
+ // header
+ int32_t nVersion{0};
+ uint256 hashMerkleRoot;
+ uint32_t nTime{0};
+ uint32_t nBits{0};
+ uint32_t nNonce{0};
+
+ CompressedHeader()
+ {
+ hashMerkleRoot.SetNull();
+ }
+
+ CompressedHeader(const CBlockHeader& header)
+ {
+ nVersion = header.nVersion;
+ hashMerkleRoot = header.hashMerkleRoot;
+ nTime = header.nTime;
+ nBits = header.nBits;
+ nNonce = header.nNonce;
+ }
+
+ CBlockHeader GetFullHeader(const uint256& hash_prev_block) {
+ CBlockHeader ret;
+ ret.nVersion = nVersion;
+ ret.hashPrevBlock = hash_prev_block;
+ ret.hashMerkleRoot = hashMerkleRoot;
+ ret.nTime = nTime;
+ ret.nBits = nBits;
+ ret.nNonce = nNonce;
+ return ret;
+ };
+};
+
+/** HeadersSyncState:
+ *
+ * We wish to download a peer's headers chain in a DoS-resistant way.
+ *
+ * The Bitcoin protocol does not offer an easy way to determine the work on a
+ * peer's chain. Currently, we can query a peer's headers by using a GETHEADERS
+ * message, and our peer can return a set of up to 2000 headers that connect to
+ * something we know. If a peer's chain has more than 2000 blocks, then we need
+ * a way to verify that the chain actually has enough work on it to be useful to
+ * us -- by being above our anti-DoS minimum-chain-work threshold -- before we
+ * commit to storing those headers in memory. Otherwise, it would be cheap for
+ * an attacker to waste all our memory by serving us low-work headers
+ * (particularly for a new node coming online for the first time).
+ *
+ * To prevent memory-DoS with low-work headers, while still always being
+ * able to reorg to whatever the most-work chain is, we require that a chain
+ * meet a work threshold before committing it to memory. We can do this by
+ * downloading a peer's headers twice, whenever we are not sure that the chain
+ * has sufficient work:
+ *
+ * - In the first download phase, called pre-synchronization, we can calculate
+ * the work on the chain as we go (just by checking the nBits value on each
+ * header, and validating the proof-of-work).
+ *
+ * - Once we have reached a header where the cumulative chain work is
+ * sufficient, we switch to downloading the headers a second time, this time
+ * processing them fully, and possibly storing them in memory.
+ *
+ * To prevent an attacker from using (eg) the honest chain to convince us that
+ * they have a high-work chain, but then feeding us an alternate set of
+ * low-difficulty headers in the second phase, we store commitments to the
+ * chain we see in the first download phase that we check in the second phase,
+ * as follows:
+ *
+ * - In phase 1 (presync), store 1 bit (using a salted hash function) for every
+ * N headers that we see. With a reasonable choice of N, this uses relatively
+ * little memory even for a very long chain.
+ *
+ * - In phase 2 (redownload), keep a lookahead buffer and only accept headers
+ * from that buffer into the block index (permanent memory usage) once they
+ * have some target number of verified commitments on top of them. With this
+ * parametrization, we can achieve a given security target for potential
+ * permanent memory usage, while choosing N to minimize memory use during the
+ * sync (temporary, per-peer storage).
+ */
+
+class HeadersSyncState {
+public:
+ ~HeadersSyncState() {}
+
+ enum class State {
+ /** PRESYNC means the peer has not yet demonstrated their chain has
+ * sufficient work and we're only building commitments to the chain they
+ * serve us. */
+ PRESYNC,
+ /** REDOWNLOAD means the peer has given us a high-enough-work chain,
+ * and now we're redownloading the headers we saw before and trying to
+ * accept them */
+ REDOWNLOAD,
+ /** We're done syncing with this peer and can discard any remaining state */
+ FINAL
+ };
+
+ /** Return the current state of our download */
+ State GetState() const { return m_download_state; }
+
+ /** Return the height reached during the PRESYNC phase */
+ int64_t GetPresyncHeight() const { return m_current_height; }
+
+ /** Return the block timestamp of the last header received during the PRESYNC phase. */
+ uint32_t GetPresyncTime() const { return m_last_header_received.nTime; }
+
+ /** Return the amount of work in the chain received during the PRESYNC phase. */
+ arith_uint256 GetPresyncWork() const { return m_current_chain_work; }
+
+ /** Construct a HeadersSyncState object representing a headers sync via this
+ * download-twice mechanism).
+ *
+ * id: node id (for logging)
+ * consensus_params: parameters needed for difficulty adjustment validation
+ * chain_start: best known fork point that the peer's headers branch from
+ * minimum_required_work: amount of chain work required to accept the chain
+ */
+ HeadersSyncState(NodeId id, const Consensus::Params& consensus_params,
+ const CBlockIndex* chain_start, const arith_uint256& minimum_required_work);
+
+ /** Result data structure for ProcessNextHeaders. */
+ struct ProcessingResult {
+ std::vector<CBlockHeader> pow_validated_headers;
+ bool success{false};
+ bool request_more{false};
+ };
+
+ /** Process a batch of headers, once a sync via this mechanism has started
+ *
+ * received_headers: headers that were received over the network for processing.
+ * Assumes the caller has already verified the headers
+ * are continuous, and has checked that each header
+ * satisfies the proof-of-work target included in the
+ * header (but not necessarily verified that the
+ * proof-of-work target is correct and passes consensus
+ * rules).
+ * full_headers_message: true if the message was at max capacity,
+ * indicating more headers may be available
+ * ProcessingResult.pow_validated_headers: will be filled in with any
+ * headers that the caller can fully process and
+ * validate now (because these returned headers are
+ * on a chain with sufficient work)
+ * ProcessingResult.success: set to false if an error is detected and the sync is
+ * aborted; true otherwise.
+ * ProcessingResult.request_more: if true, the caller is suggested to call
+ * NextHeadersRequestLocator and send a getheaders message using it.
+ */
+ ProcessingResult ProcessNextHeaders(const std::vector<CBlockHeader>&
+ received_headers, bool full_headers_message);
+
+ /** Issue the next GETHEADERS message to our peer.
+ *
+ * This will return a locator appropriate for the current sync object, to continue the
+ * synchronization phase it is in.
+ */
+ CBlockLocator NextHeadersRequestLocator() const;
+
+private:
+ /** Clear out all download state that might be in progress (freeing any used
+ * memory), and mark this object as no longer usable.
+ */
+ void Finalize();
+
+ /**
+ * Only called in PRESYNC.
+ * Validate the work on the headers we received from the network, and
+ * store commitments for later. Update overall state with successfully
+ * processed headers.
+ * On failure, this invokes Finalize() and returns false.
+ */
+ bool ValidateAndStoreHeadersCommitments(const std::vector<CBlockHeader>& headers);
+
+ /** In PRESYNC, process and update state for a single header */
+ bool ValidateAndProcessSingleHeader(const CBlockHeader& current);
+
+ /** In REDOWNLOAD, check a header's commitment (if applicable) and add to
+ * buffer for later processing */
+ bool ValidateAndStoreRedownloadedHeader(const CBlockHeader& header);
+
+ /** Return a set of headers that satisfy our proof-of-work threshold */
+ std::vector<CBlockHeader> PopHeadersReadyForAcceptance();
+
+private:
+ /** NodeId of the peer (used for log messages) **/
+ const NodeId m_id;
+
+ /** We use the consensus params in our anti-DoS calculations */
+ const Consensus::Params& m_consensus_params;
+
+ /** Store the last block in our block index that the peer's chain builds from */
+ const CBlockIndex* m_chain_start{nullptr};
+
+ /** Minimum work that we're looking for on this chain. */
+ const arith_uint256 m_minimum_required_work;
+
+ /** Work that we've seen so far on the peer's chain */
+ arith_uint256 m_current_chain_work;
+
+ /** m_hasher is a salted hasher for making our 1-bit commitments to headers we've seen. */
+ const SaltedTxidHasher m_hasher;
+
+ /** A queue of commitment bits, created during the 1st phase, and verified during the 2nd. */
+ bitdeque<> m_header_commitments;
+
+ /** The (secret) offset on the heights for which to create commitments.
+ *
+ * m_header_commitments entries are created at any height h for which
+ * (h % HEADER_COMMITMENT_PERIOD) == m_commit_offset. */
+ const unsigned m_commit_offset;
+
+ /** m_max_commitments is a bound we calculate on how long an honest peer's chain could be,
+ * given the MTP rule.
+ *
+ * Any peer giving us more headers than this will have its sync aborted. This serves as a
+ * memory bound on m_header_commitments. */
+ uint64_t m_max_commitments{0};
+
+ /** Store the latest header received while in PRESYNC (initialized to m_chain_start) */
+ CBlockHeader m_last_header_received;
+
+ /** Height of m_last_header_received */
+ int64_t m_current_height{0};
+
+ /** During phase 2 (REDOWNLOAD), we buffer redownloaded headers in memory
+ * until enough commitments have been verified; those are stored in
+ * m_redownloaded_headers */
+ std::deque<CompressedHeader> m_redownloaded_headers;
+
+ /** Height of last header in m_redownloaded_headers */
+ int64_t m_redownload_buffer_last_height{0};
+
+ /** Hash of last header in m_redownloaded_headers (initialized to
+ * m_chain_start). We have to cache it because we don't have hashPrevBlock
+ * available in a CompressedHeader.
+ */
+ uint256 m_redownload_buffer_last_hash;
+
+ /** The hashPrevBlock entry for the first header in m_redownloaded_headers
+ * We need this to reconstruct the full header when it's time for
+ * processing.
+ */
+ uint256 m_redownload_buffer_first_prev_hash;
+
+ /** The accumulated work on the redownloaded chain. */
+ arith_uint256 m_redownload_chain_work;
+
+ /** Set this to true once we encounter the target blockheader during phase
+ * 2 (REDOWNLOAD). At this point, we can process and store all remaining
+ * headers still in m_redownloaded_headers.
+ */
+ bool m_process_all_remaining_headers{false};
+
+ /** Current state of our headers sync. */
+ State m_download_state{State::PRESYNC};
+};
+
+#endif // BITCOIN_HEADERSSYNC_H
diff --git a/src/i2p.cpp b/src/i2p.cpp
index c45bcc15d2..f7d480988b 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -12,11 +12,11 @@
#include <netaddress.h>
#include <netbase.h>
#include <random.h>
-#include <util/strencodings.h>
#include <tinyformat.h>
#include <util/readwritefile.h>
#include <util/sock.h>
#include <util/spanparsing.h>
+#include <util/strencodings.h>
#include <util/system.h>
#include <chrono>
@@ -115,8 +115,19 @@ namespace sam {
Session::Session(const fs::path& private_key_file,
const CService& control_host,
CThreadInterrupt* interrupt)
- : m_private_key_file(private_key_file), m_control_host(control_host), m_interrupt(interrupt),
- m_control_sock(std::make_unique<Sock>(INVALID_SOCKET))
+ : m_private_key_file{private_key_file},
+ m_control_host{control_host},
+ m_interrupt{interrupt},
+ m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
+ m_transient{false}
+{
+}
+
+Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
+ : m_control_host{control_host},
+ m_interrupt{interrupt},
+ m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
+ m_transient{true}
{
}
@@ -355,29 +366,47 @@ void Session::CreateIfNotCreatedAlready()
return;
}
- Log("Creating SAM session with %s", m_control_host.ToString());
+ const auto session_type = m_transient ? "transient" : "persistent";
+ const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs
+
+ Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToString());
auto sock = Hello();
- const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
- if (read_ok) {
- m_private_key.assign(data.begin(), data.end());
+ if (m_transient) {
+ // The destination (private key) is generated upon session creation and returned
+ // in the reply in DESTINATION=.
+ const Reply& reply = SendRequestAndGetReply(
+ *sock,
+ strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT", session_id));
+
+ m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
} else {
- GenerateAndSavePrivateKey(*sock);
- }
+ // Read our persistent destination (private key) from disk or generate
+ // one and save it to disk. Then use it when creating the session.
+ const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
+ if (read_ok) {
+ m_private_key.assign(data.begin(), data.end());
+ } else {
+ GenerateAndSavePrivateKey(*sock);
+ }
- const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
- const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
+ const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
- SendRequestAndGetReply(*sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
- session_id, private_key_b64));
+ SendRequestAndGetReply(*sock,
+ strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
+ session_id,
+ private_key_b64));
+ }
m_my_addr = CService(DestBinToAddr(MyDestination()), I2P_SAM31_PORT);
m_session_id = session_id;
m_control_sock = std::move(sock);
- LogPrintfCategory(BCLog::I2P, "SAM session created: session id=%s, my address=%s\n",
- m_session_id, m_my_addr.ToString());
+ Log("%s SAM session %s created, my address=%s",
+ Capitalize(session_type),
+ m_session_id,
+ m_my_addr.ToString());
}
std::unique_ptr<Sock> Session::StreamAccept()
@@ -405,9 +434,9 @@ void Session::Disconnect()
{
if (m_control_sock->Get() != INVALID_SOCKET) {
if (m_session_id.empty()) {
- Log("Destroying incomplete session");
+ Log("Destroying incomplete SAM session");
} else {
- Log("Destroying session %s", m_session_id);
+ Log("Destroying SAM session %s", m_session_id);
}
}
m_control_sock = std::make_unique<Sock>(INVALID_SOCKET);
diff --git a/src/i2p.h b/src/i2p.h
index eb0a10103d..ebbcb437da 100644
--- a/src/i2p.h
+++ b/src/i2p.h
@@ -71,6 +71,19 @@ public:
CThreadInterrupt* interrupt);
/**
+ * Construct a transient session which will generate its own I2P private key
+ * rather than read the one from disk (it will not be saved on disk either and
+ * will be lost once this object is destroyed). This will not initiate any IO,
+ * the session will be lazily created later when first used.
+ * @param[in] control_host Location of the SAM proxy.
+ * @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
+ * possible and executing methods throw an exception. Notice: only a pointer to the
+ * `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
+ * `Session` object.
+ */
+ Session(const CService& control_host, CThreadInterrupt* interrupt);
+
+ /**
* Destroy the session, closing the internally used sockets. The sockets that have been
* returned by `Accept()` or `Connect()` will not be closed, but they will be closed by
* the SAM proxy because the session is destroyed. So they will return an error next time
@@ -262,6 +275,12 @@ private:
* SAM session id.
*/
std::string m_session_id GUARDED_BY(m_mutex);
+
+ /**
+ * Whether this is a transient session (the I2P private key will not be
+ * read or written to disk).
+ */
+ const bool m_transient;
};
} // namespace sam
diff --git a/src/init.cpp b/src/init.cpp
index 480b8c7705..137d98026e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -560,7 +560,7 @@ void SetupServerArgs(ArgsManager& argsman)
SetupChainParamsBaseOptions(argsman);
argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
- argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and replacement policy. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 2c31e12ada..dbdb21eb91 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -260,7 +260,7 @@ public:
//! Register handler for header tip messages.
using NotifyHeaderTipFn =
- std::function<void(SynchronizationState, interfaces::BlockTip tip, double verification_progress)>;
+ std::function<void(SynchronizationState, interfaces::BlockTip tip, bool presync)>;
virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
//! Get and set internal node context. Useful for testing, but not
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
index 510a1f9edc..520d0e8e75 100644
--- a/src/kernel/chainstatemanager_opts.h
+++ b/src/kernel/chainstatemanager_opts.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
#define BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
+#include <util/time.h>
+
#include <cstdint>
#include <functional>
@@ -19,7 +21,7 @@ namespace kernel {
*/
struct ChainstateManagerOpts {
const CChainParams& chainparams;
- const std::function<int64_t()> adjusted_time_callback{nullptr};
+ const std::function<NodeClock::time_point()> adjusted_time_callback{nullptr};
};
} // namespace kernel
diff --git a/src/logging.cpp b/src/logging.cpp
index 73c4e458bd..92fc31917f 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -165,6 +165,7 @@ const CLogCategoryDesc LogCategories[] =
#endif
{BCLog::UTIL, "util"},
{BCLog::BLOCKSTORE, "blockstorage"},
+ {BCLog::HEADERSSYNC, "headerssync"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
@@ -263,6 +264,8 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
return "util";
case BCLog::LogFlags::BLOCKSTORE:
return "blockstorage";
+ case BCLog::LogFlags::HEADERSSYNC:
+ return "headerssync";
case BCLog::LogFlags::ALL:
return "all";
}
diff --git a/src/logging.h b/src/logging.h
index 50869ad89a..a7f18f7560 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -65,6 +65,7 @@ namespace BCLog {
#endif
UTIL = (1 << 25),
BLOCKSTORE = (1 << 26),
+ HEADERSSYNC = (1 << 27),
ALL = ~(uint32_t)0,
};
enum class Level {
diff --git a/src/net.cpp b/src/net.cpp
index 865ce2ea97..535a5ce8e2 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -484,18 +484,27 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
Proxy proxy;
CAddress addr_bind;
assert(!addr_bind.IsValid());
+ std::unique_ptr<i2p::sam::Session> i2p_transient_session;
if (addrConnect.IsValid()) {
+ const bool use_proxy{GetProxy(addrConnect.GetNetwork(), proxy)};
bool proxyConnectionFailed = false;
- if (addrConnect.GetNetwork() == NET_I2P && m_i2p_sam_session.get() != nullptr) {
+ if (addrConnect.GetNetwork() == NET_I2P && use_proxy) {
i2p::Connection conn;
- if (m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed)) {
- connected = true;
+
+ if (m_i2p_sam_session) {
+ connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
+ } else {
+ i2p_transient_session = std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
+ connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
+ }
+
+ if (connected) {
sock = std::move(conn.sock);
addr_bind = CAddress{conn.me, NODE_NONE};
}
- } else if (GetProxy(addrConnect.GetNetwork(), proxy)) {
+ } else if (use_proxy) {
sock = CreateSock(proxy.proxy);
if (!sock) {
return nullptr;
@@ -546,7 +555,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addr_bind,
pszDest ? pszDest : "",
conn_type,
- /*inbound_onion=*/false);
+ /*inbound_onion=*/false,
+ std::move(i2p_transient_session));
pnode->AddRef();
// We're making a new connection, harvest entropy from the time (and our peer count)
@@ -563,6 +573,7 @@ void CNode::CloseSocketDisconnect()
LogPrint(BCLog::NET, "disconnecting peer=%d\n", id);
m_sock.reset();
}
+ m_i2p_sam_session.reset();
}
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
@@ -786,7 +797,8 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
return msg;
}
-void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) {
+void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) const
+{
// create dbl-sha256 checksum
uint256 hash = Hash(msg.data);
@@ -2258,7 +2270,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
Proxy i2p_sam;
- if (GetProxy(NET_I2P, i2p_sam)) {
+ if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
i2p_sam.proxy, &interruptNet);
}
@@ -2332,7 +2344,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
// Process messages
threadMessageHandler = std::thread(&util::TraceThread, "msghand", [this] { ThreadMessageHandler(); });
- if (connOptions.m_i2p_accept_incoming && m_i2p_sam_session.get() != nullptr) {
+ if (m_i2p_sam_session) {
threadI2PAcceptIncoming =
std::thread(&util::TraceThread, "i2paccept", [this] { ThreadI2PAcceptIncoming(); });
}
@@ -2701,20 +2713,29 @@ ServiceFlags CConnman::GetLocalServices() const
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
-CNode::CNode(NodeId idIn, std::shared_ptr<Sock> sock, const CAddress& addrIn,
- uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn,
- const CAddress& addrBindIn, const std::string& addrNameIn,
- ConnectionType conn_type_in, bool inbound_onion)
- : m_sock{sock},
+CNode::CNode(NodeId idIn,
+ std::shared_ptr<Sock> sock,
+ const CAddress& addrIn,
+ uint64_t nKeyedNetGroupIn,
+ uint64_t nLocalHostNonceIn,
+ const CAddress& addrBindIn,
+ const std::string& addrNameIn,
+ ConnectionType conn_type_in,
+ bool inbound_onion,
+ std::unique_ptr<i2p::sam::Session>&& i2p_sam_session)
+ : m_deserializer{std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), idIn, SER_NETWORK, INIT_PROTO_VERSION))},
+ m_serializer{std::make_unique<V1TransportSerializer>(V1TransportSerializer())},
+ m_sock{sock},
m_connected{GetTime<std::chrono::seconds>()},
- addr(addrIn),
- addrBind(addrBindIn),
+ addr{addrIn},
+ addrBind{addrBindIn},
m_addr_name{addrNameIn.empty() ? addr.ToStringIPPort() : addrNameIn},
- m_inbound_onion(inbound_onion),
- nKeyedNetGroup(nKeyedNetGroupIn),
- id(idIn),
- nLocalHostNonce(nLocalHostNonceIn),
- m_conn_type(conn_type_in)
+ m_inbound_onion{inbound_onion},
+ nKeyedNetGroup{nKeyedNetGroupIn},
+ id{idIn},
+ nLocalHostNonce{nLocalHostNonceIn},
+ m_conn_type{conn_type_in},
+ m_i2p_sam_session{std::move(i2p_sam_session)}
{
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
@@ -2727,9 +2748,6 @@ CNode::CNode(NodeId idIn, std::shared_ptr<Sock> sock, const CAddress& addrIn,
} else {
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
-
- m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION));
- m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
}
bool CConnman::NodeFullyConnected(const CNode* pnode)
diff --git a/src/net.h b/src/net.h
index 2036e9078c..abdfef85f8 100644
--- a/src/net.h
+++ b/src/net.h
@@ -325,13 +325,13 @@ public:
class TransportSerializer {
public:
// prepare message for transport (header construction, error-correction computation, payload encryption, etc.)
- virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) = 0;
+ virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) const = 0;
virtual ~TransportSerializer() {}
};
-class V1TransportSerializer : public TransportSerializer {
+class V1TransportSerializer : public TransportSerializer {
public:
- void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) override;
+ void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) const override;
};
/** Information about a peer */
@@ -341,10 +341,10 @@ class CNode
friend struct ConnmanTestMsg;
public:
- std::unique_ptr<TransportDeserializer> m_deserializer;
- std::unique_ptr<TransportSerializer> m_serializer;
+ const std::unique_ptr<TransportDeserializer> m_deserializer; // Used only by SocketHandler thread
+ const std::unique_ptr<const TransportSerializer> m_serializer;
- NetPermissionFlags m_permissionFlags{NetPermissionFlags::None};
+ NetPermissionFlags m_permissionFlags{NetPermissionFlags::None}; // treated as const outside of fuzz tester
/**
* Socket used for communication with the node.
@@ -368,7 +368,7 @@ public:
RecursiveMutex cs_vProcessMsg;
std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg);
- size_t nProcessQueueSize{0};
+ size_t nProcessQueueSize GUARDED_BY(cs_vProcessMsg){0};
RecursiveMutex cs_sendProcessing;
@@ -393,7 +393,7 @@ public:
* from the wire. This cleaned string can safely be logged or displayed.
*/
std::string cleanSubVer GUARDED_BY(m_subver_mutex){};
- bool m_prefer_evict{false}; // This peer is preferred for eviction.
+ bool m_prefer_evict{false}; // This peer is preferred for eviction. (treated as const)
bool HasPermission(NetPermissionFlags permission) const {
return NetPermissions::HasFlag(m_permissionFlags, permission);
}
@@ -513,10 +513,16 @@ public:
* criterium in CConnman::AttemptToEvictConnection. */
std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()};
- CNode(NodeId id, std::shared_ptr<Sock> sock, const CAddress& addrIn,
- uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn,
- const CAddress& addrBindIn, const std::string& addrNameIn,
- ConnectionType conn_type_in, bool inbound_onion);
+ CNode(NodeId id,
+ std::shared_ptr<Sock> sock,
+ const CAddress& addrIn,
+ uint64_t nKeyedNetGroupIn,
+ uint64_t nLocalHostNonceIn,
+ const CAddress& addrBindIn,
+ const std::string& addrNameIn,
+ ConnectionType conn_type_in,
+ bool inbound_onion,
+ std::unique_ptr<i2p::sam::Session>&& i2p_sam_session = nullptr);
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
@@ -596,6 +602,18 @@ private:
mapMsgTypeSize mapSendBytesPerMsgType GUARDED_BY(cs_vSend);
mapMsgTypeSize mapRecvBytesPerMsgType GUARDED_BY(cs_vRecv);
+
+ /**
+ * If an I2P session is created per connection (for outbound transient I2P
+ * connections) then it is stored here so that it can be destroyed when the
+ * socket is closed. I2P sessions involve a data/transport socket (in `m_sock`)
+ * and a control socket (in `m_i2p_sam_session`). For transient sessions, once
+ * the data socket is closed, the control socket is not going to be used anymore
+ * and is just taking up resources. So better close it as soon as `m_sock` is
+ * closed.
+ * Otherwise this unique_ptr is empty.
+ */
+ std::unique_ptr<i2p::sam::Session> m_i2p_sam_session GUARDED_BY(m_sock_mutex);
};
/**
@@ -1072,7 +1090,8 @@ private:
/**
* I2P SAM session.
- * Used to accept incoming and make outgoing I2P connections.
+ * Used to accept incoming and make outgoing I2P connections from a persistent
+ * address.
*/
std::unique_ptr<i2p::sam::Session> m_i2p_sam_session;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b39412158f..746e3bedde 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -14,6 +14,7 @@
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <hash.h>
+#include <headerssync.h>
#include <index/blockfilterindex.h>
#include <merkleblock.h>
#include <netbase.h>
@@ -289,7 +290,7 @@ struct Peer {
* non-wtxid-relay peers, wtxid for wtxid-relay peers). We use the
* mempool to sort transactions in dependency order before relay, so
* this does not have to be sorted. */
- std::set<uint256> m_tx_inventory_to_send;
+ std::set<uint256> m_tx_inventory_to_send GUARDED_BY(m_tx_inventory_mutex);
/** Whether the peer has requested us to send our complete mempool. Only
* permitted if the peer has NetPermissionFlags::Mempool. See BIP35. */
bool m_send_mempool GUARDED_BY(m_tx_inventory_mutex){false};
@@ -381,6 +382,15 @@ struct Peer {
/** Time of the last getheaders message to this peer */
NodeClock::time_point m_last_getheaders_timestamp{};
+ /** Protects m_headers_sync **/
+ Mutex m_headers_sync_mutex;
+ /** Headers-sync state for this peer (eg for initial sync, or syncing large
+ * reorgs) **/
+ std::unique_ptr<HeadersSyncState> m_headers_sync PT_GUARDED_BY(m_headers_sync_mutex) GUARDED_BY(m_headers_sync_mutex) {};
+
+ /** Whether we've sent our peer a sendheaders message. **/
+ std::atomic<bool> m_sent_sendheaders{false};
+
explicit Peer(NodeId id, ServiceFlags our_services)
: m_id{id}
, m_our_services{our_services}
@@ -503,9 +513,9 @@ public:
/** Implement NetEventsInterface */
void InitializeNode(CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
- void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex);
bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing)
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
@@ -522,7 +532,7 @@ public:
void UnitTestMisbehaving(NodeId peer_id, int howmuch) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), howmuch, ""); };
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex);
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
private:
@@ -581,18 +591,70 @@ private:
void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
- /** Process a single headers message from a peer. */
+ /** Process a single headers message from a peer.
+ *
+ * @param[in] pfrom CNode of the peer
+ * @param[in] peer The peer sending us the headers
+ * @param[in] headers The headers received. Note that this may be modified within ProcessHeadersMessage.
+ * @param[in] via_compact_block Whether this header came in via compact block handling.
+ */
void ProcessHeadersMessage(CNode& pfrom, Peer& peer,
- const std::vector<CBlockHeader>& headers,
+ std::vector<CBlockHeader>&& headers,
bool via_compact_block)
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
/** Various helpers for headers processing, invoked by ProcessHeadersMessage() */
+ /** Return true if headers are continuous and have valid proof-of-work (DoS points assigned on failure) */
+ bool CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, Peer& peer);
+ /** Calculate an anti-DoS work threshold for headers chains */
+ arith_uint256 GetAntiDoSWorkThreshold();
/** Deal with state tracking and headers sync for peers that send the
* occasional non-connecting header (this can happen due to BIP 130 headers
* announcements for blocks interacting with the 2hr (MAX_FUTURE_BLOCK_TIME) rule). */
void HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer, const std::vector<CBlockHeader>& headers);
/** Return true if the headers connect to each other, false otherwise */
bool CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const;
+ /** Try to continue a low-work headers sync that has already begun.
+ * Assumes the caller has already verified the headers connect, and has
+ * checked that each header satisfies the proof-of-work target included in
+ * the header.
+ * @param[in] peer The peer we're syncing with.
+ * @param[in] pfrom CNode of the peer
+ * @param[in,out] headers The headers to be processed.
+ * @return True if the passed in headers were successfully processed
+ * as the continuation of a low-work headers sync in progress;
+ * false otherwise.
+ * If false, the passed in headers will be returned back to
+ * the caller.
+ * If true, the returned headers may be empty, indicating
+ * there is no more work for the caller to do; or the headers
+ * may be populated with entries that have passed anti-DoS
+ * checks (and therefore may be validated for block index
+ * acceptance by the caller).
+ */
+ bool IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom,
+ std::vector<CBlockHeader>& headers)
+ EXCLUSIVE_LOCKS_REQUIRED(peer.m_headers_sync_mutex, !m_headers_presync_mutex);
+ /** Check work on a headers chain to be processed, and if insufficient,
+ * initiate our anti-DoS headers sync mechanism.
+ *
+ * @param[in] peer The peer whose headers we're processing.
+ * @param[in] pfrom CNode of the peer
+ * @param[in] chain_start_header Where these headers connect in our index.
+ * @param[in,out] headers The headers to be processed.
+ *
+ * @return True if chain was low work and a headers sync was
+ * initiated (and headers will be empty after calling); false
+ * otherwise.
+ */
+ bool TryLowWorkHeadersSync(Peer& peer, CNode& pfrom,
+ const CBlockIndex* chain_start_header,
+ std::vector<CBlockHeader>& headers)
+ EXCLUSIVE_LOCKS_REQUIRED(!peer.m_headers_sync_mutex, !m_peer_mutex, !m_headers_presync_mutex);
+
+ /** Return true if the given header is an ancestor of
+ * m_chainman.m_best_header or our current tip */
+ bool IsAncestorOfBestHeaderOrTip(const CBlockIndex* header) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
/** Request further headers from this peer with a given locator.
* We don't issue a getheaders message if we have a recent one outstanding.
* This returns true if a getheaders is actually sent, and false otherwise.
@@ -623,6 +685,9 @@ private:
/** Send `addr` messages on a regular schedule. */
void MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time);
+ /** Send a single `sendheaders` message, after we have completed headers sync with a peer. */
+ void MaybeSendSendHeaders(CNode& node, Peer& peer);
+
/** Relay (gossip) an address to a few randomly chosen nodes.
*
* @param[in] originator The id of the peer that sent us the address. We don't want to relay it back.
@@ -648,7 +713,7 @@ private:
std::atomic<int> m_best_height{-1};
/** Next time to check for stale tip */
- std::chrono::seconds m_stale_tip_check_time{0s};
+ std::chrono::seconds m_stale_tip_check_time GUARDED_BY(cs_main){0s};
/** Whether this node is running in -blocksonly mode */
const bool m_ignore_incoming_txs;
@@ -657,7 +722,7 @@ private:
/** Whether we've completed initial sync yet, for determining when to turn
* on extra block-relay-only peers. */
- bool m_initial_sync_finished{false};
+ bool m_initial_sync_finished GUARDED_BY(cs_main){false};
/** Protects m_peer_map. This mutex must not be locked while holding a lock
* on any of the mutexes inside a Peer object. */
@@ -779,6 +844,24 @@ private:
std::shared_ptr<const CBlockHeaderAndShortTxIDs> m_most_recent_compact_block GUARDED_BY(m_most_recent_block_mutex);
uint256 m_most_recent_block_hash GUARDED_BY(m_most_recent_block_mutex);
+ // Data about the low-work headers synchronization, aggregated from all peers' HeadersSyncStates.
+ /** Mutex guarding the other m_headers_presync_* variables. */
+ Mutex m_headers_presync_mutex;
+ /** A type to represent statistics about a peer's low-work headers sync.
+ *
+ * - The first field is the total verified amount of work in that synchronization.
+ * - The second is:
+ * - nullopt: the sync is in REDOWNLOAD phase (phase 2).
+ * - {height, timestamp}: the sync has the specified tip height and block timestamp (phase 1).
+ */
+ using HeadersPresyncStats = std::pair<arith_uint256, std::optional<std::pair<int64_t, uint32_t>>>;
+ /** Statistics for all peers in low-work headers sync. */
+ std::map<NodeId, HeadersPresyncStats> m_headers_presync_stats GUARDED_BY(m_headers_presync_mutex) {};
+ /** The peer with the most-work entry in m_headers_presync_stats. */
+ NodeId m_headers_presync_bestpeer GUARDED_BY(m_headers_presync_mutex) {-1};
+ /** The m_headers_presync_stats improved, and needs signalling. */
+ std::atomic_bool m_headers_presync_should_signal{false};
+
/** Height of the highest block announced using BIP 152 high-bandwidth mode. */
int m_highest_fast_announce{0};
@@ -816,7 +899,7 @@ private:
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, peer.m_getdata_requests_mutex) LOCKS_EXCLUDED(::cs_main);
/** Process a new block. Perform any post-processing housekeeping */
- void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing);
+ void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked);
/** Relay map (txid or wtxid -> CTransactionRef) */
typedef std::map<uint256, CTransactionRef> MapRelay;
@@ -1151,7 +1234,7 @@ bool PeerManagerImpl::TipMayBeStale()
bool PeerManagerImpl::CanDirectFetch()
{
- return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - m_chainparams.GetConsensus().nPowTargetSpacing * 20;
+ return m_chainman.ActiveChain().Tip()->Time() > GetAdjustedTime() - m_chainparams.GetConsensus().PowTargetSpacing() * 20;
}
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1437,6 +1520,10 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
// fSuccessfullyConnected set.
m_addrman.Connected(node.addr);
}
+ {
+ LOCK(m_headers_presync_mutex);
+ m_headers_presync_stats.erase(nodeid);
+ }
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
}
@@ -1501,6 +1588,12 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
stats.m_addr_processed = peer->m_addr_processed.load();
stats.m_addr_rate_limited = peer->m_addr_rate_limited.load();
stats.m_addr_relay_enabled = peer->m_addr_relay_enabled.load();
+ {
+ LOCK(peer->m_headers_sync_mutex);
+ if (peer->m_headers_sync) {
+ stats.presync_height = peer->m_headers_sync->GetPresyncHeight();
+ }
+ }
return true;
}
@@ -1544,6 +1637,10 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
switch (state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
+ case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
+ // We didn't try to process the block because the header chain may have
+ // too little work.
+ break;
// The node is providing invalid data:
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
@@ -2263,6 +2360,35 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlo
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCKTXN, resp));
}
+bool PeerManagerImpl::CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, Peer& peer)
+{
+ // Do these headers have proof-of-work matching what's claimed?
+ if (!HasValidProofOfWork(headers, consensusParams)) {
+ Misbehaving(peer, 100, "header with invalid proof of work");
+ return false;
+ }
+
+ // Are these headers connected to each other?
+ if (!CheckHeadersAreContinuous(headers)) {
+ Misbehaving(peer, 20, "non-continuous headers sequence");
+ return false;
+ }
+ return true;
+}
+
+arith_uint256 PeerManagerImpl::GetAntiDoSWorkThreshold()
+{
+ arith_uint256 near_chaintip_work = 0;
+ LOCK(cs_main);
+ if (m_chainman.ActiveChain().Tip() != nullptr) {
+ const CBlockIndex *tip = m_chainman.ActiveChain().Tip();
+ // Use a 144 block buffer, so that we'll accept headers that fork from
+ // near our tip.
+ near_chaintip_work = tip->nChainWork - std::min<arith_uint256>(144*GetBlockProof(*tip), tip->nChainWork);
+ }
+ return std::max(near_chaintip_work, arith_uint256(nMinimumChainWork));
+}
+
/**
* Special handling for unconnecting headers that might be part of a block
* announcement.
@@ -2285,7 +2411,7 @@ void PeerManagerImpl::HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer,
nodestate->nUnconnectingHeaders++;
// Try to fill in the missing headers.
- if (MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), peer)) {
+ if (MaybeSendGetHeaders(pfrom, GetLocator(m_chainman.m_best_header), peer)) {
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
@@ -2316,6 +2442,146 @@ bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>&
return true;
}
+bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom, std::vector<CBlockHeader>& headers)
+{
+ if (peer.m_headers_sync) {
+ auto result = peer.m_headers_sync->ProcessNextHeaders(headers, headers.size() == MAX_HEADERS_RESULTS);
+ if (result.request_more) {
+ auto locator = peer.m_headers_sync->NextHeadersRequestLocator();
+ // If we were instructed to ask for a locator, it should not be empty.
+ Assume(!locator.vHave.empty());
+ if (!locator.vHave.empty()) {
+ // It should be impossible for the getheaders request to fail,
+ // because we should have cleared the last getheaders timestamp
+ // when processing the headers that triggered this call. But
+ // it may be possible to bypass this via compactblock
+ // processing, so check the result before logging just to be
+ // safe.
+ bool sent_getheaders = MaybeSendGetHeaders(pfrom, locator, peer);
+ if (sent_getheaders) {
+ LogPrint(BCLog::NET, "more getheaders (from %s) to peer=%d\n",
+ locator.vHave.front().ToString(), pfrom.GetId());
+ } else {
+ LogPrint(BCLog::NET, "error sending next getheaders (from %s) to continue sync with peer=%d\n",
+ locator.vHave.front().ToString(), pfrom.GetId());
+ }
+ }
+ }
+
+ if (peer.m_headers_sync->GetState() == HeadersSyncState::State::FINAL) {
+ peer.m_headers_sync.reset(nullptr);
+
+ // Delete this peer's entry in m_headers_presync_stats.
+ // If this is m_headers_presync_bestpeer, it will be replaced later
+ // by the next peer that triggers the else{} branch below.
+ LOCK(m_headers_presync_mutex);
+ m_headers_presync_stats.erase(pfrom.GetId());
+ } else {
+ // Build statistics for this peer's sync.
+ HeadersPresyncStats stats;
+ stats.first = peer.m_headers_sync->GetPresyncWork();
+ if (peer.m_headers_sync->GetState() == HeadersSyncState::State::PRESYNC) {
+ stats.second = {peer.m_headers_sync->GetPresyncHeight(),
+ peer.m_headers_sync->GetPresyncTime()};
+ }
+
+ // Update statistics in stats.
+ LOCK(m_headers_presync_mutex);
+ m_headers_presync_stats[pfrom.GetId()] = stats;
+ auto best_it = m_headers_presync_stats.find(m_headers_presync_bestpeer);
+ bool best_updated = false;
+ if (best_it == m_headers_presync_stats.end()) {
+ // If the cached best peer is outdated, iterate over all remaining ones (including
+ // newly updated one) to find the best one.
+ NodeId peer_best{-1};
+ const HeadersPresyncStats* stat_best{nullptr};
+ for (const auto& [peer, stat] : m_headers_presync_stats) {
+ if (!stat_best || stat > *stat_best) {
+ peer_best = peer;
+ stat_best = &stat;
+ }
+ }
+ m_headers_presync_bestpeer = peer_best;
+ best_updated = (peer_best == pfrom.GetId());
+ } else if (best_it->first == pfrom.GetId() || stats > best_it->second) {
+ // pfrom was and remains the best peer, or pfrom just became best.
+ m_headers_presync_bestpeer = pfrom.GetId();
+ best_updated = true;
+ }
+ if (best_updated && stats.second.has_value()) {
+ // If the best peer updated, and it is in its first phase, signal.
+ m_headers_presync_should_signal = true;
+ }
+ }
+
+ if (result.success) {
+ // We only overwrite the headers passed in if processing was
+ // successful.
+ headers.swap(result.pow_validated_headers);
+ }
+
+ return result.success;
+ }
+ // Either we didn't have a sync in progress, or something went wrong
+ // processing these headers, or we are returning headers to the caller to
+ // process.
+ return false;
+}
+
+bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlockIndex* chain_start_header, std::vector<CBlockHeader>& headers)
+{
+ // Calculate the total work on this chain.
+ arith_uint256 total_work = chain_start_header->nChainWork + CalculateHeadersWork(headers);
+
+ // Our dynamic anti-DoS threshold (minimum work required on a headers chain
+ // before we'll store it)
+ arith_uint256 minimum_chain_work = GetAntiDoSWorkThreshold();
+
+ // Avoid DoS via low-difficulty-headers by only processing if the headers
+ // are part of a chain with sufficient work.
+ if (total_work < minimum_chain_work) {
+ // Only try to sync with this peer if their headers message was full;
+ // otherwise they don't have more headers after this so no point in
+ // trying to sync their too-little-work chain.
+ if (headers.size() == MAX_HEADERS_RESULTS) {
+ // Note: we could advance to the last header in this set that is
+ // known to us, rather than starting at the first header (which we
+ // may already have); however this is unlikely to matter much since
+ // ProcessHeadersMessage() already handles the case where all
+ // headers in a received message are already known and are
+ // ancestors of m_best_header or chainActive.Tip(), by skipping
+ // this logic in that case. So even if the first header in this set
+ // of headers is known, some header in this set must be new, so
+ // advancing to the first unknown header would be a small effect.
+ LOCK(peer.m_headers_sync_mutex);
+ peer.m_headers_sync.reset(new HeadersSyncState(peer.m_id, m_chainparams.GetConsensus(),
+ chain_start_header, minimum_chain_work));
+
+ // Now a HeadersSyncState object for tracking this synchronization is created,
+ // process the headers using it as normal.
+ return IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers);
+ } else {
+ LogPrint(BCLog::NET, "Ignoring low-work chain (height=%u) from peer=%d\n", chain_start_header->nHeight + headers.size(), pfrom.GetId());
+ // Since this is a low-work headers chain, no further processing is required.
+ headers = {};
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PeerManagerImpl::IsAncestorOfBestHeaderOrTip(const CBlockIndex* header)
+{
+ if (header == nullptr) {
+ return false;
+ } else if (m_chainman.m_best_header != nullptr && header == m_chainman.m_best_header->GetAncestor(header->nHeight)) {
+ return true;
+ } else if (m_chainman.ActiveChain().Contains(header)) {
+ return true;
+ }
+ return false;
+}
+
bool PeerManagerImpl::MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer)
{
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
@@ -2461,21 +2727,73 @@ void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
}
void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
- const std::vector<CBlockHeader>& headers,
+ std::vector<CBlockHeader>&& headers,
bool via_compact_block)
{
- const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
size_t nCount = headers.size();
if (nCount == 0) {
// Nothing interesting. Stop asking this peers for more headers.
+ // If we were in the middle of headers sync, receiving an empty headers
+ // message suggests that the peer suddenly has nothing to give us
+ // (perhaps it reorged to our chain). Clear download state for this peer.
+ LOCK(peer.m_headers_sync_mutex);
+ if (peer.m_headers_sync) {
+ peer.m_headers_sync.reset(nullptr);
+ LOCK(m_headers_presync_mutex);
+ m_headers_presync_stats.erase(pfrom.GetId());
+ }
+ return;
+ }
+
+ // Before we do any processing, make sure these pass basic sanity checks.
+ // We'll rely on headers having valid proof-of-work further down, as an
+ // anti-DoS criteria (note: this check is required before passing any
+ // headers into HeadersSyncState).
+ if (!CheckHeadersPoW(headers, m_chainparams.GetConsensus(), peer)) {
+ // Misbehaving() calls are handled within CheckHeadersPoW(), so we can
+ // just return. (Note that even if a header is announced via compact
+ // block, the header itself should be valid, so this type of error can
+ // always be punished.)
return;
}
const CBlockIndex *pindexLast = nullptr;
+ // We'll set already_validated_work to true if these headers are
+ // successfully processed as part of a low-work headers sync in progress
+ // (either in PRESYNC or REDOWNLOAD phase).
+ // If true, this will mean that any headers returned to us (ie during
+ // REDOWNLOAD) can be validated without further anti-DoS checks.
+ bool already_validated_work = false;
+
+ // If we're in the middle of headers sync, let it do its magic.
+ bool have_headers_sync = false;
+ {
+ LOCK(peer.m_headers_sync_mutex);
+
+ already_validated_work = IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers);
+
+ // The headers we passed in may have been:
+ // - untouched, perhaps if no headers-sync was in progress, or some
+ // failure occurred
+ // - erased, such as if the headers were successfully processed and no
+ // additional headers processing needs to take place (such as if we
+ // are still in PRESYNC)
+ // - replaced with headers that are now ready for validation, such as
+ // during the REDOWNLOAD phase of a low-work headers sync.
+ // So just check whether we still have headers that we need to process,
+ // or not.
+ if (headers.empty()) {
+ return;
+ }
+
+ have_headers_sync = !!peer.m_headers_sync;
+ }
+
// Do these headers connect to something in our block index?
- bool headers_connect_blockindex{WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) != nullptr)};
+ const CBlockIndex *chain_start_header{WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock))};
+ bool headers_connect_blockindex{chain_start_header != nullptr};
if (!headers_connect_blockindex) {
if (nCount <= MAX_BLOCKS_TO_ANNOUNCE) {
@@ -2489,28 +2807,51 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
return;
}
+ // If the headers we received are already in memory and an ancestor of
+ // m_best_header or our tip, skip anti-DoS checks. These headers will not
+ // use any more memory (and we are not leaking information that could be
+ // used to fingerprint us).
+ const CBlockIndex *last_received_header{nullptr};
+ {
+ LOCK(cs_main);
+ last_received_header = m_chainman.m_blockman.LookupBlockIndex(headers.back().GetHash());
+ if (IsAncestorOfBestHeaderOrTip(last_received_header)) {
+ already_validated_work = true;
+ }
+ }
+
// At this point, the headers connect to something in our block index.
- if (!CheckHeadersAreContinuous(headers)) {
- Misbehaving(peer, 20, "non-continuous headers sequence");
+ // Do anti-DoS checks to determine if we should process or store for later
+ // processing.
+ if (!already_validated_work && TryLowWorkHeadersSync(peer, pfrom,
+ chain_start_header, headers)) {
+ // If we successfully started a low-work headers sync, then there
+ // should be no headers to process any further.
+ Assume(headers.empty());
return;
}
+ // At this point, we have a set of headers with sufficient work on them
+ // which can be processed.
+
// If we don't have the last header, then this peer will have given us
// something new (if these headers are valid).
- bool received_new_header{WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(headers.back().GetHash()) == nullptr)};
+ bool received_new_header{last_received_header != nullptr};
+ // Now process all the headers.
BlockValidationState state;
- if (!m_chainman.ProcessNewBlockHeaders(headers, state, &pindexLast)) {
+ if (!m_chainman.ProcessNewBlockHeaders(headers, /*min_pow_checked=*/true, state, &pindexLast)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
return;
}
}
+ Assume(pindexLast);
- // Consider fetching more headers.
- if (nCount == MAX_HEADERS_RESULTS) {
+ // Consider fetching more headers if we are not using our headers-sync mechanism.
+ if (nCount == MAX_HEADERS_RESULTS && !have_headers_sync) {
// Headers message had its maximum size; the peer may have more headers.
- if (MaybeSendGetHeaders(pfrom, WITH_LOCK(m_chainman.GetMutex(), return m_chainman.ActiveChain().GetLocator(pindexLast)), peer)) {
+ if (MaybeSendGetHeaders(pfrom, GetLocator(pindexLast), peer)) {
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
}
@@ -2771,10 +3112,10 @@ void PeerManagerImpl::ProcessGetCFCheckPt(CNode& node, Peer& peer, CDataStream&
m_connman.PushMessage(&node, std::move(msg));
}
-void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing)
+void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked)
{
bool new_block{false};
- m_chainman.ProcessNewBlock(block, force_processing, &new_block);
+ m_chainman.ProcessNewBlock(block, force_processing, min_pow_checked, &new_block);
if (new_block) {
node.m_last_block_time = GetTime<std::chrono::seconds>();
} else {
@@ -3032,13 +3373,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.ConnectionTypeAsString());
}
- if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) {
- // Tell our peer we prefer to receive headers rather than inv's
- // We send this to non-NODE NETWORK peers as well, because even
- // non-NODE NETWORK peers can announce blocks (such as pruning
- // nodes)
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
- }
if (pfrom.GetCommonVersion() >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version 2 cmpctblocks.
// However, we do not request new block announcements using
@@ -3285,7 +3619,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// use if we turned on sync with all peers).
CNodeState& state{*Assert(State(pfrom.GetId()))};
if (state.fSyncStarted || (!peer->m_inv_triggered_getheaders_before_sync && *best_block != m_last_block_inv_triggering_headers_sync)) {
- if (MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *peer)) {
+ if (MaybeSendGetHeaders(pfrom, GetLocator(m_chainman.m_best_header), *peer)) {
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n",
m_chainman.m_best_header->nHeight, best_block->ToString(),
pfrom.GetId());
@@ -3749,12 +4083,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(cs_main);
- if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
+ const CBlockIndex* prev_block = m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock);
+ if (!prev_block) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
- MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *peer);
+ MaybeSendGetHeaders(pfrom, GetLocator(m_chainman.m_best_header), *peer);
}
return;
+ } else if (prev_block->nChainWork + CalculateHeadersWork({cmpctblock.header}) < GetAntiDoSWorkThreshold()) {
+ // If we get a low-work header in a compact block, we can ignore it.
+ LogPrint(BCLog::NET, "Ignoring low-work compact block from peer %d\n", pfrom.GetId());
+ return;
}
if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) {
@@ -3764,7 +4103,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
- if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, &pindex)) {
+ if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, /*min_pow_checked=*/true, state, &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block=*/true, "invalid header via cmpctblock");
return;
@@ -3931,7 +4270,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// we have a chain with at least nMinimumChainWork), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
- ProcessBlock(pfrom, pblock, /*force_processing=*/true);
+ ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true);
LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid()
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) {
// Clear download state for this block, which is in
@@ -4014,7 +4353,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
- ProcessBlock(pfrom, pblock, /*force_processing=*/true);
+ ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true);
}
return;
}
@@ -4045,7 +4384,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- return ProcessHeadersMessage(pfrom, *peer, headers, /*via_compact_block=*/false);
+ ProcessHeadersMessage(pfrom, *peer, std::move(headers), /*via_compact_block=*/false);
+
+ // Check if the headers presync progress needs to be reported to validation.
+ // This needs to be done without holding the m_headers_presync_mutex lock.
+ if (m_headers_presync_should_signal.exchange(false)) {
+ HeadersPresyncStats stats;
+ {
+ LOCK(m_headers_presync_mutex);
+ auto it = m_headers_presync_stats.find(m_headers_presync_bestpeer);
+ if (it != m_headers_presync_stats.end()) stats = it->second;
+ }
+ if (stats.second) {
+ m_chainman.ReportHeadersPresync(stats.first, stats.second->first, stats.second->second);
+ }
+ }
+
+ return;
}
if (msg_type == NetMsgType::BLOCK)
@@ -4063,6 +4418,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
+ bool min_pow_checked = false;
{
LOCK(cs_main);
// Always process the block if we requested it, since we may
@@ -4073,8 +4429,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
+
+ // Check work on this block against our anti-dos thresholds.
+ const CBlockIndex* prev_block = m_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock);
+ if (prev_block && prev_block->nChainWork + CalculateHeadersWork({pblock->GetBlockHeader()}) >= GetAntiDoSWorkThreshold()) {
+ min_pow_checked = true;
+ }
}
- ProcessBlock(pfrom, pblock, forceProcessing);
+ ProcessBlock(pfrom, pblock, forceProcessing, min_pow_checked);
return;
}
@@ -4502,7 +4864,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seco
// getheaders in-flight already, in which case the peer should
// still respond to us with a sufficiently high work chain tip.
MaybeSendGetHeaders(pto,
- m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev),
+ GetLocator(state.m_chain_sync.m_work_header->pprev),
peer);
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
state.m_chain_sync.m_sent_getheaders = true;
@@ -4759,6 +5121,27 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
}
}
+void PeerManagerImpl::MaybeSendSendHeaders(CNode& node, Peer& peer)
+{
+ // Delay sending SENDHEADERS (BIP 130) until we're done with an
+ // initial-headers-sync with this peer. Receiving headers announcements for
+ // new blocks while trying to sync their headers chain is problematic,
+ // because of the state tracking done.
+ if (!peer.m_sent_sendheaders && node.GetCommonVersion() >= SENDHEADERS_VERSION) {
+ LOCK(cs_main);
+ CNodeState &state = *State(node.GetId());
+ if (state.pindexBestKnownBlock != nullptr &&
+ state.pindexBestKnownBlock->nChainWork > nMinimumChainWork) {
+ // Tell our peer we prefer to receive headers rather than inv's
+ // We send this to non-NODE NETWORK peers as well, because even
+ // non-NODE NETWORK peers can announce blocks (such as pruning
+ // nodes)
+ m_connman.PushMessage(&node, CNetMsgMaker(node.GetCommonVersion()).Make(NetMsgType::SENDHEADERS));
+ peer.m_sent_sendheaders = true;
+ }
+ }
+}
+
void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::microseconds current_time)
{
if (m_ignore_incoming_txs) return;
@@ -4880,6 +5263,8 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
MaybeSendAddr(*pto, *peer, current_time);
+ MaybeSendSendHeaders(*pto, *peer);
+
{
LOCK(cs_main);
@@ -4913,7 +5298,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (!state.fSyncStarted && CanServeBlocks(*peer) && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
- if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
+ if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->Time() > GetAdjustedTime() - 24h) {
const CBlockIndex* pindexStart = m_chainman.m_best_header;
/* If possible, start at the block preceding the currently
best known header. This ensures that we always get a
@@ -4924,7 +5309,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
got back an empty response. */
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
- if (MaybeSendGetHeaders(*pto, m_chainman.ActiveChain().GetLocator(pindexStart), *peer)) {
+ if (MaybeSendGetHeaders(*pto, GetLocator(pindexStart), *peer)) {
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height);
state.fSyncStarted = true;
@@ -4933,7 +5318,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
// to maintain precision
std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
- (GetAdjustedTime() - m_chainman.m_best_header->GetBlockTime()) / consensusParams.nPowTargetSpacing
+ Ticks<std::chrono::seconds>(GetAdjustedTime() - m_chainman.m_best_header->Time()) / consensusParams.nPowTargetSpacing
);
nSyncStarted++;
}
@@ -5250,7 +5635,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Check for headers sync timeouts
if (state.fSyncStarted && state.m_headers_sync_timeout < std::chrono::microseconds::max()) {
// Detect whether this is a stalling initial-headers-sync peer
- if (m_chainman.m_best_header->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
+ if (m_chainman.m_best_header->Time() <= GetAdjustedTime() - 24h) {
if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (m_num_preferred_download_peers - state.fPreferredDownload >= 1)) {
// Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer,
// and we have others we could be using instead.
diff --git a/src/net_processing.h b/src/net_processing.h
index bcda9614d4..0a882b1e53 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -35,6 +35,7 @@ struct CNodeStateStats {
uint64_t m_addr_rate_limited = 0;
bool m_addr_relay_enabled{false};
ServiceFlags their_services;
+ int64_t presync_height{-1};
};
class PeerManager : public CValidationInterface, public NetEventsInterface
diff --git a/src/node/interface_ui.cpp b/src/node/interface_ui.cpp
index 370cde84f8..fa90d6fda7 100644
--- a/src/node/interface_ui.cpp
+++ b/src/node/interface_ui.cpp
@@ -53,7 +53,7 @@ void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(s, i); }
-void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(s, i); }
+void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, int64_t height, int64_t timestamp, bool presync) { return g_ui_signals.NotifyHeaderTip(s, height, timestamp, presync); }
void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); }
bool InitError(const bilingual_str& str)
diff --git a/src/node/interface_ui.h b/src/node/interface_ui.h
index 37c0f6392b..316d75167e 100644
--- a/src/node/interface_ui.h
+++ b/src/node/interface_ui.h
@@ -105,7 +105,7 @@ public:
ADD_SIGNALS_DECL_WRAPPER(NotifyBlockTip, void, SynchronizationState, const CBlockIndex*);
/** Best header has changed */
- ADD_SIGNALS_DECL_WRAPPER(NotifyHeaderTip, void, SynchronizationState, const CBlockIndex*);
+ ADD_SIGNALS_DECL_WRAPPER(NotifyHeaderTip, void, SynchronizationState, int64_t height, int64_t timestamp, bool presync);
/** Banlist did change. */
ADD_SIGNALS_DECL_WRAPPER(BannedListChanged, void, void);
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 2c845d0127..aa7ddec770 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -377,9 +377,8 @@ public:
std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) override
{
return MakeHandler(
- ::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) {
- fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
- /* verification progress is unused when a header was received */ 0);
+ ::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, int64_t height, int64_t timestamp, bool presync) {
+ fn(sync_state, BlockTip{(int)height, timestamp, uint256{}}, presync);
}));
}
NodeContext* context() override { return m_context; }
@@ -400,7 +399,7 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<Rec
if (block.m_max_time) *block.m_max_time = index->GetBlockTimeMax();
if (block.m_mtp_time) *block.m_mtp_time = index->GetMedianTimePast();
if (block.m_in_active_chain) *block.m_in_active_chain = active[index->nHeight] == index;
- if (block.m_locator) { *block.m_locator = active.GetLocator(index); }
+ if (block.m_locator) { *block.m_locator = GetLocator(index); }
if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active);
if (block.m_data) {
REVERSE_LOCK(lock);
@@ -527,8 +526,7 @@ public:
{
LOCK(::cs_main);
const CBlockIndex* index = chainman().m_blockman.LookupBlockIndex(block_hash);
- if (!index) return {};
- return chainman().ActiveChain().GetLocator(index);
+ return GetLocator(index);
}
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp
index d8b4ce4bf4..8c929e5e0d 100644
--- a/src/node/mempool_args.cpp
+++ b/src/node/mempool_args.cpp
@@ -46,7 +46,7 @@ std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& argsman, con
if (auto hours = argsman.GetIntArg("-mempoolexpiry")) mempool_opts.expiry = std::chrono::hours{*hours};
- // incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
+ // incremental relay fee sets the minimum feerate increase necessary for replacement in the mempool
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
if (argsman.IsArgSet("-incrementalrelayfee")) {
if (std::optional<CAmount> inc_relay_fee = ParseMoney(argsman.GetArg("-incrementalrelayfee", ""))) {
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index 9db10feae4..f04742deeb 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -30,7 +30,7 @@ namespace node {
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
int64_t nOldTime = pblock->nTime;
- int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast() + 1, GetAdjustedTime());
+ int64_t nNewTime{std::max<int64_t>(pindexPrev->GetMedianTimePast() + 1, TicksSinceEpoch<std::chrono::seconds>(GetAdjustedTime()))};
if (nOldTime < nNewTime) {
pblock->nTime = nNewTime;
@@ -133,7 +133,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
}
- pblock->nTime = GetAdjustedTime();
+ pblock->nTime = TicksSinceEpoch<std::chrono::seconds>(GetAdjustedTime());
m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
int nPackagesSelected = 0;
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 3d2660b081..29764ea2d9 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -31,7 +31,7 @@ static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{82};
static constexpr unsigned int MAX_P2SH_SIGOPS{15};
/** The maximum number of sigops we're willing to relay/mine in a single tx */
static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/5};
-/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or BIP 125 replacement **/
+/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or replacement **/
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000};
/** Default for -bytespersigop */
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index e25f5c7c5b..6098caced9 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -65,15 +65,15 @@ std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
uint64_t nConflictingCount = 0;
for (const auto& mi : iters_conflicting) {
nConflictingCount += mi->GetCountWithDescendants();
- // BIP125 Rule #5: don't consider replacing more than MAX_BIP125_REPLACEMENT_CANDIDATES
+ // Rule #5: don't consider replacing more than MAX_REPLACEMENT_CANDIDATES
// entries from the mempool. This potentially overestimates the number of actual
// descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
// times), but we just want to be conservative to avoid doing too much work.
- if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) {
+ if (nConflictingCount > MAX_REPLACEMENT_CANDIDATES) {
return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
txid.ToString(),
nConflictingCount,
- MAX_BIP125_REPLACEMENT_CANDIDATES);
+ MAX_REPLACEMENT_CANDIDATES);
}
}
// Calculate the set of all transactions that would have to be evicted.
@@ -96,7 +96,7 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
}
for (unsigned int j = 0; j < tx.vin.size(); j++) {
- // BIP125 Rule #2: We don't want to accept replacements that require low feerate junk to be
+ // Rule #2: We don't want to accept replacements that require low feerate junk to be
// mined first. Ideally we'd keep track of the ancestor feerates and make the decision
// based on that, but for now requiring all new inputs to be confirmed works.
//
@@ -162,7 +162,7 @@ std::optional<std::string> PaysForRBF(CAmount original_fees,
CFeeRate relay_fee,
const uint256& txid)
{
- // BIP125 Rule #3: The replacement fees must be greater than or equal to fees of the
+ // Rule #3: The replacement fees must be greater than or equal to fees of the
// transactions it replaces, otherwise the bandwidth used by those conflicting transactions
// would not be paid for.
if (replacement_fees < original_fees) {
@@ -170,7 +170,7 @@ std::optional<std::string> PaysForRBF(CAmount original_fees,
txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
}
- // BIP125 Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
+ // Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
// vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
// increasing the fee by tiny amounts.
CAmount additional_fees = replacement_fees - original_fees;
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 07f68c8fd4..28c4e4bf9b 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -19,9 +19,9 @@
class CFeeRate;
class uint256;
-/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
+/** Maximum number of transactions that can be replaced by RBF (Rule #5). This includes all
* mempool conflicts and their descendants. */
-static constexpr uint32_t MAX_BIP125_REPLACEMENT_CANDIDATES{100};
+static constexpr uint32_t MAX_REPLACEMENT_CANDIDATES{100};
/** The rbf state of unconfirmed transactions */
enum class RBFTransactionState {
@@ -47,24 +47,25 @@ enum class RBFTransactionState {
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
-/** Get all descendants of iters_conflicting. Also enforce BIP125 Rule #5, "The number of original
- * transactions to be replaced and their descendant transactions which will be evicted from the
- * mempool must not exceed a total of 100 transactions." Quit as early as possible. There cannot be
- * more than MAX_BIP125_REPLACEMENT_CANDIDATES potential entries.
+/** Get all descendants of iters_conflicting. Checks that there are no more than
+ * MAX_REPLACEMENT_CANDIDATES potential entries. May overestimate if the entries in
+ * iters_conflicting have overlapping descendants.
* @param[in] iters_conflicting The set of iterators to mempool entries.
* @param[out] all_conflicts Populated with all the mempool entries that would be replaced,
- * which includes descendants of iters_conflicting. Not cleared at
- * the start; any existing mempool entries will remain in the set.
- * @returns an error message if Rule #5 is broken, otherwise a std::nullopt.
+ * which includes iters_conflicting and all entries' descendants.
+ * Not cleared at the start; any existing mempool entries will
+ * remain in the set.
+ * @returns an error message if MAX_REPLACEMENT_CANDIDATES may be exceeded, otherwise a std::nullopt.
*/
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting,
CTxMemPool::setEntries& all_conflicts)
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
-/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input
- * was included in one of the original transactions."
- * @returns error message if Rule #2 is broken, otherwise std::nullopt. */
+/** The replacement transaction may only include an unconfirmed input if that input was included in
+ * one of the original transactions.
+ * @returns error message if tx spends unconfirmed inputs not also spent by iters_conflicting,
+ * otherwise std::nullopt. */
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting)
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
@@ -90,9 +91,8 @@ std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries&
std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
CFeeRate replacement_feerate, const uint256& txid);
-/** Enforce BIP125 Rule #3 "The replacement transaction pays an absolute fee of at least the sum
- * paid by the original transactions." Enforce BIP125 Rule #4 "The replacement transaction must also
- * pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting."
+/** The replacement transaction must pay more fees than the original transactions. The additional
+ * fees must pay for the replacement's bandwidth at or above the incremental relay feerate.
* @param[in] original_fees Total modified fees of original transaction(s).
* @param[in] replacement_fees Total modified fees of replacement transaction(s).
* @param[in] replacement_vsize Total virtual size of replacement transaction(s).
diff --git a/src/pow.cpp b/src/pow.cpp
index 1414d37564..c0449cac74 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -71,6 +71,57 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF
return bnNew.GetCompact();
}
+// Check that on difficulty adjustments, the new difficulty does not increase
+// or decrease beyond the permitted limits.
+bool PermittedDifficultyTransition(const Consensus::Params& params, int64_t height, uint32_t old_nbits, uint32_t new_nbits)
+{
+ if (params.fPowAllowMinDifficultyBlocks) return true;
+
+ if (height % params.DifficultyAdjustmentInterval() == 0) {
+ int64_t smallest_timespan = params.nPowTargetTimespan/4;
+ int64_t largest_timespan = params.nPowTargetTimespan*4;
+
+ const arith_uint256 pow_limit = UintToArith256(params.powLimit);
+ arith_uint256 observed_new_target;
+ observed_new_target.SetCompact(new_nbits);
+
+ // Calculate the largest difficulty value possible:
+ arith_uint256 largest_difficulty_target;
+ largest_difficulty_target.SetCompact(old_nbits);
+ largest_difficulty_target *= largest_timespan;
+ largest_difficulty_target /= params.nPowTargetTimespan;
+
+ if (largest_difficulty_target > pow_limit) {
+ largest_difficulty_target = pow_limit;
+ }
+
+ // Round and then compare this new calculated value to what is
+ // observed.
+ arith_uint256 maximum_new_target;
+ maximum_new_target.SetCompact(largest_difficulty_target.GetCompact());
+ if (maximum_new_target < observed_new_target) return false;
+
+ // Calculate the smallest difficulty value possible:
+ arith_uint256 smallest_difficulty_target;
+ smallest_difficulty_target.SetCompact(old_nbits);
+ smallest_difficulty_target *= smallest_timespan;
+ smallest_difficulty_target /= params.nPowTargetTimespan;
+
+ if (smallest_difficulty_target > pow_limit) {
+ smallest_difficulty_target = pow_limit;
+ }
+
+ // Round and then compare this new calculated value to what is
+ // observed.
+ arith_uint256 minimum_new_target;
+ minimum_new_target.SetCompact(smallest_difficulty_target.GetCompact());
+ if (minimum_new_target > observed_new_target) return false;
+ } else if (old_nbits != new_nbits) {
+ return false;
+ }
+ return true;
+}
+
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
bool fNegative;
diff --git a/src/pow.h b/src/pow.h
index 1d802cd01e..44b9d673ef 100644
--- a/src/pow.h
+++ b/src/pow.h
@@ -20,4 +20,18 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
+/**
+ * Return false if the proof-of-work requirement specified by new_nbits at a
+ * given height is not possible, given the proof-of-work on the prior block as
+ * specified by old_nbits.
+ *
+ * This function only checks that the new value is within a factor of 4 of the
+ * old value for blocks at the difficulty adjustment interval, and otherwise
+ * requires the values to be the same.
+ *
+ * Always returns true on networks where min difficulty blocks are allowed,
+ * such as regtest/testnet.
+ */
+bool PermittedDifficultyTransition(const Consensus::Params& params, int64_t height, uint32_t old_nbits, uint32_t new_nbits);
+
#endif // BITCOIN_POW_H
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 2d10853607..76aba6c899 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -9,6 +9,7 @@
#include <primitives/transaction.h>
#include <serialize.h>
#include <uint256.h>
+#include <util/time.h>
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
@@ -52,6 +53,11 @@ public:
uint256 GetHash() const;
+ NodeSeconds Time() const
+ {
+ return NodeSeconds{std::chrono::seconds{nTime}};
+ }
+
int64_t GetBlockTime() const
{
return (int64_t)nTime;
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 139405170b..bdd1cc2aff 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -199,12 +199,7 @@ static std::string serviceFlagToStr(size_t bit)
// Not using default, so we get warned when a case is missing
}
- std::ostringstream stream;
- stream.imbue(std::locale::classic());
- stream << "UNKNOWN[";
- stream << "2^" << bit;
- stream << "]";
- return stream.str();
+ return strprintf("UNKNOWN[2^%u]", bit);
}
std::vector<std::string> serviceFlagsToStr(uint64_t flags)
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 33c60deafb..cc01e4d54a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -75,6 +75,7 @@ Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin)
Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
Q_DECLARE_METATYPE(SynchronizationState)
+Q_DECLARE_METATYPE(SyncType)
Q_DECLARE_METATYPE(uint256)
static void RegisterMetaTypes()
@@ -82,6 +83,7 @@ static void RegisterMetaTypes()
// Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
qRegisterMetaType<bool*>();
qRegisterMetaType<SynchronizationState>();
+ qRegisterMetaType<SyncType>();
#ifdef ENABLE_WALLET
qRegisterMetaType<WalletModel*>();
#endif
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 90f228803c..1c1328e000 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -615,8 +615,8 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH
connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections);
connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
- modalOverlay->setKnownBestHeight(tip_info->header_height, QDateTime::fromSecsSinceEpoch(tip_info->header_time));
- setNumBlocks(tip_info->block_height, QDateTime::fromSecsSinceEpoch(tip_info->block_time), tip_info->verification_progress, false, SynchronizationState::INIT_DOWNLOAD);
+ modalOverlay->setKnownBestHeight(tip_info->header_height, QDateTime::fromSecsSinceEpoch(tip_info->header_time), /*presync=*/false);
+ setNumBlocks(tip_info->block_height, QDateTime::fromSecsSinceEpoch(tip_info->block_time), tip_info->verification_progress, SyncType::BLOCK_SYNC, SynchronizationState::INIT_DOWNLOAD);
connect(_clientModel, &ClientModel::numBlocksChanged, this, &BitcoinGUI::setNumBlocks);
// Receive and report messages from client model
@@ -1026,6 +1026,13 @@ void BitcoinGUI::updateHeadersSyncProgressLabel()
progressBarLabel->setText(tr("Syncing Headers (%1%)…").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1)));
}
+void BitcoinGUI::updateHeadersPresyncProgressLabel(int64_t height, const QDateTime& blockDate)
+{
+ int estHeadersLeft = blockDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
+ if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC)
+ progressBarLabel->setText(tr("Pre-syncing Headers (%1%)…").arg(QString::number(100.0 / (height+estHeadersLeft)*height, 'f', 1)));
+}
+
void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
{
if (!clientModel || !clientModel->getOptionsModel())
@@ -1039,7 +1046,7 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
GUIUtil::ShowModalDialogAsynchronously(dlg);
}
-void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
+void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
{
// Disabling macOS App Nap on initial sync, disk and reindex operations.
#ifdef Q_OS_MACOS
@@ -1052,8 +1059,8 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
if (modalOverlay)
{
- if (header)
- modalOverlay->setKnownBestHeight(count, blockDate);
+ if (synctype != SyncType::BLOCK_SYNC)
+ modalOverlay->setKnownBestHeight(count, blockDate, synctype == SyncType::HEADER_PRESYNC);
else
modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
}
@@ -1067,7 +1074,10 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
enum BlockSource blockSource = clientModel->getBlockSource();
switch (blockSource) {
case BlockSource::NETWORK:
- if (header) {
+ if (synctype == SyncType::HEADER_PRESYNC) {
+ updateHeadersPresyncProgressLabel(count, blockDate);
+ return;
+ } else if (synctype == SyncType::HEADER_SYNC) {
updateHeadersSyncProgressLabel();
return;
}
@@ -1075,7 +1085,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
updateHeadersSyncProgressLabel();
break;
case BlockSource::DISK:
- if (header) {
+ if (synctype != SyncType::BLOCK_SYNC) {
progressBarLabel->setText(tr("Indexing blocks on disk…"));
} else {
progressBarLabel->setText(tr("Processing blocks on disk…"));
@@ -1085,7 +1095,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
progressBarLabel->setText(tr("Reindexing blocks on disk…"));
break;
case BlockSource::NONE:
- if (header) {
+ if (synctype != SyncType::BLOCK_SYNC) {
return;
}
progressBarLabel->setText(tr("Connecting to peers…"));
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index b2e13245e1..912e9b95aa 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -10,6 +10,7 @@
#endif
#include <qt/bitcoinunits.h>
+#include <qt/clientmodel.h>
#include <qt/guiutil.h>
#include <qt/optionsdialog.h>
@@ -28,7 +29,6 @@
#include <memory>
-class ClientModel;
class NetworkStyle;
class Notificator;
class OptionsModel;
@@ -208,6 +208,7 @@ private:
void updateNetworkState();
void updateHeadersSyncProgressLabel();
+ void updateHeadersPresyncProgressLabel(int64_t height, const QDateTime& blockDate);
/** Open the OptionsDialog on the specified tab index */
void openOptionsDialogWithTab(OptionsDialog::Tab tab);
@@ -226,7 +227,7 @@ public Q_SLOTS:
/** Set network state shown in the UI */
void setNetworkActive(bool network_active);
/** Set number of blocks and last block date shown in the UI */
- void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
+ void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state);
/** Notify the user of an event from the core network or transaction handling code.
@param[in] title the message box / notification title
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index f41da519df..092ffe7e5b 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -215,26 +215,26 @@ QString ClientModel::blocksDir() const
return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());
}
-void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header)
+void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, SyncType synctype)
{
- if (header) {
+ if (synctype == SyncType::HEADER_SYNC) {
// cache best headers time and height to reduce future cs_main locks
cachedBestHeaderHeight = tip.block_height;
cachedBestHeaderTime = tip.block_time;
- } else {
+ } else if (synctype == SyncType::BLOCK_SYNC) {
m_cached_num_blocks = tip.block_height;
WITH_LOCK(m_cached_tip_mutex, m_cached_tip_blocks = tip.block_hash;);
}
// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
- const bool throttle = (sync_state != SynchronizationState::POST_INIT && !header) || sync_state == SynchronizationState::INIT_REINDEX;
+ const bool throttle = (sync_state != SynchronizationState::POST_INIT && synctype == SyncType::BLOCK_SYNC) || sync_state == SynchronizationState::INIT_REINDEX;
const int64_t now = throttle ? GetTimeMillis() : 0;
- int64_t& nLastUpdateNotification = header ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
+ int64_t& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) {
return;
}
- Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, header, sync_state);
+ Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, synctype, sync_state);
nLastUpdateNotification = now;
}
@@ -264,11 +264,11 @@ void ClientModel::subscribeToCoreSignals()
});
m_handler_notify_block_tip = m_node.handleNotifyBlockTip(
[this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
- TipChanged(sync_state, tip, verification_progress, /*header=*/false);
+ TipChanged(sync_state, tip, verification_progress, SyncType::BLOCK_SYNC);
});
m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(
- [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
- TipChanged(sync_state, tip, verification_progress, /*header=*/true);
+ [this](SynchronizationState sync_state, interfaces::BlockTip tip, bool presync) {
+ TipChanged(sync_state, tip, /*verification_progress=*/0.0, presync ? SyncType::HEADER_PRESYNC : SyncType::HEADER_SYNC);
});
}
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 81f03a58ec..4a6abd6a76 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -37,6 +37,12 @@ enum class BlockSource {
NETWORK
};
+enum class SyncType {
+ HEADER_PRESYNC,
+ HEADER_SYNC,
+ BLOCK_SYNC
+};
+
enum NumConnections {
CONNECTIONS_NONE = 0,
CONNECTIONS_IN = (1U << 0),
@@ -105,13 +111,13 @@ private:
//! A thread to interact with m_node asynchronously
QThread* const m_thread;
- void TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_tip_mutex);
+ void TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, SyncType synctype) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_tip_mutex);
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
Q_SIGNALS:
void numConnectionsChanged(int count);
- void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state);
+ void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
void networkActiveChanged(bool networkActive);
void alertsChanged(const QString &warnings);
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index 97ee75a31f..dfa33764f6 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -78,13 +78,16 @@ bool ModalOverlay::event(QEvent* ev) {
return QWidget::event(ev);
}
-void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate)
+void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate, bool presync)
{
- if (count > bestHeaderHeight) {
+ if (!presync && count > bestHeaderHeight) {
bestHeaderHeight = count;
bestHeaderDate = blockDate;
UpdateHeaderSyncLabel();
}
+ if (presync) {
+ UpdateHeaderPresyncLabel(count, blockDate);
+ }
}
void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress)
@@ -158,6 +161,11 @@ void ModalOverlay::UpdateHeaderSyncLabel() {
ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)…").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1)));
}
+void ModalOverlay::UpdateHeaderPresyncLabel(int height, const QDateTime& blockDate) {
+ int est_headers_left = blockDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
+ ui->numberOfBlocksLeft->setText(tr("Unknown. Pre-syncing Headers (%1, %2%)…").arg(height).arg(QString::number(100.0 / (height + est_headers_left) * height, 'f', 1)));
+}
+
void ModalOverlay::toggleVisibility()
{
showHide(layerIsVisible, true);
diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h
index 1d8af5cbf6..682c94cd01 100644
--- a/src/qt/modaloverlay.h
+++ b/src/qt/modaloverlay.h
@@ -26,7 +26,7 @@ public:
~ModalOverlay();
void tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress);
- void setKnownBestHeight(int count, const QDateTime& blockDate);
+ void setKnownBestHeight(int count, const QDateTime& blockDate, bool presync);
// will show or hide the modal layer
void showHide(bool hide = false, bool userRequested = false);
@@ -52,6 +52,7 @@ private:
bool userClosed;
QPropertyAnimation m_animation;
void UpdateHeaderSyncLabel();
+ void UpdateHeaderPresyncLabel(int height, const QDateTime& blockDate);
};
#endif // BITCOIN_QT_MODALOVERLAY_H
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 70fccdef1c..8c0b8cc3ab 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -661,7 +661,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
setNumConnections(model->getNumConnections());
connect(model, &ClientModel::numConnectionsChanged, this, &RPCConsole::setNumConnections);
- setNumBlocks(bestblock_height, QDateTime::fromSecsSinceEpoch(bestblock_date), verification_progress, false);
+ setNumBlocks(bestblock_height, QDateTime::fromSecsSinceEpoch(bestblock_date), verification_progress, SyncType::BLOCK_SYNC);
connect(model, &ClientModel::numBlocksChanged, this, &RPCConsole::setNumBlocks);
updateNetworkState();
@@ -973,9 +973,9 @@ void RPCConsole::setNetworkActive(bool networkActive)
updateNetworkState();
}
-void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers)
+void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype)
{
- if (!headers) {
+ if (synctype == SyncType::BLOCK_SYNC) {
ui->numberOfBlocks->setText(QString::number(count));
ui->lastBlockTime->setText(blockDate.toString());
}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 1a54fe0cad..a3c713e966 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -9,6 +9,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <qt/clientmodel.h>
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
@@ -19,7 +20,6 @@
#include <QThread>
#include <QWidget>
-class ClientModel;
class PlatformStyle;
class RPCExecutor;
class RPCTimerInterface;
@@ -121,7 +121,7 @@ public Q_SLOTS:
/** Set network state shown in the UI */
void setNetworkActive(bool networkActive);
/** Set number of blocks and last block date shown in the UI */
- void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
+ void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype);
/** Set size (number of transactions and memory usage) of the mempool in the UI */
void setMempoolSize(long numberOfTxs, size_t dynUsage);
/** Go forward or back in history */
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index a75c1098a5..53c352b393 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -839,7 +839,7 @@ void SendCoinsDialog::updateCoinControlState()
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
}
-void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
+void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state) {
if (sync_state == SynchronizationState::POST_INIT) {
updateSmartFeeLabel();
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index b58d4690a0..dcdf189532 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_QT_SENDCOINSDIALOG_H
#define BITCOIN_QT_SENDCOINSDIALOG_H
+#include <qt/clientmodel.h>
#include <qt/walletmodel.h>
#include <QDialog>
@@ -12,7 +13,6 @@
#include <QString>
#include <QTimer>
-class ClientModel;
class PlatformStyle;
class SendCoinsEntry;
class SendCoinsRecipient;
@@ -111,7 +111,7 @@ private Q_SLOTS:
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
void updateFeeSectionControls();
- void updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
+ void updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state);
void updateSmartFeeLabel();
Q_SIGNALS:
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 02b51ce0a0..db09a0c7b6 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -255,7 +255,7 @@ static std::vector<RPCResult> MempoolEntryDescription()
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
- RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
+ RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability.\n"},
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
};
}
@@ -690,7 +690,7 @@ static RPCHelpMan getmempoolinfo()
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
- {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kvB"},
+ {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
{RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection"},
}},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 91feb2c24b..1ad704a490 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -132,7 +132,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- if (!chainman.ProcessNewBlock(shared_pblock, true, nullptr)) {
+ if (!chainman.ProcessNewBlock(shared_pblock, /*force_processing=*/true, /*min_pow_checked=*/true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
}
@@ -981,7 +981,7 @@ static RPCHelpMan submitblock()
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*new_block=*/&new_block);
+ bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
@@ -1023,7 +1023,7 @@ static RPCHelpMan submitheader()
}
BlockValidationState state;
- chainman.ProcessNewBlockHeaders({h}, state);
+ chainman.ProcessNewBlockHeaders({h}, /*min_pow_checked=*/true, state);
if (state.IsValid()) return UniValue::VNULL;
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 3b234bc317..88584ff25f 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -132,6 +132,7 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"},
{RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"},
{RPCResult::Type::NUM, "startingheight", /*optional=*/true, "The starting height (block) of the peer"},
+ {RPCResult::Type::NUM, "presynced_headers", /*optional=*/true, "The current height of header pre-synchronization with this peer, or -1 if no low-work sync is in progress"},
{RPCResult::Type::NUM, "synced_headers", /*optional=*/true, "The last header we have in common with this peer"},
{RPCResult::Type::NUM, "synced_blocks", /*optional=*/true, "The last block we have in common with this peer"},
{RPCResult::Type::ARR, "inflight", /*optional=*/true, "",
@@ -226,6 +227,7 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("bip152_hb_from", stats.m_bip152_highbandwidth_from);
if (fStateStats) {
obj.pushKV("startingheight", statestats.m_starting_height);
+ obj.pushKV("presynced_headers", statestats.presync_height);
obj.pushKV("synced_headers", statestats.nSyncHeight);
obj.pushKV("synced_blocks", statestats.nCommonHeight);
UniValue heights(UniValue::VARR);
@@ -605,7 +607,7 @@ static RPCHelpMan getnetworkinfo()
}},
}},
{RPCResult::Type::NUM, "relayfee", "minimum relay fee rate for transactions in " + CURRENCY_UNIT + "/kvB"},
- {RPCResult::Type::NUM, "incrementalfee", "minimum fee rate increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kvB"},
+ {RPCResult::Type::NUM, "incrementalfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
{RPCResult::Type::ARR, "localaddresses", "list of local addresses",
{
{RPCResult::Type::OBJ, "", "",
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index f91578d684..ba910cc945 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -307,10 +307,10 @@ using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CM
class DeferringSignatureChecker : public BaseSignatureChecker
{
protected:
- BaseSignatureChecker& m_checker;
+ const BaseSignatureChecker& m_checker;
public:
- DeferringSignatureChecker(BaseSignatureChecker& checker) : m_checker(checker) {}
+ DeferringSignatureChecker(const BaseSignatureChecker& checker) : m_checker(checker) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
{
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 4014ebadbc..5da0d076d8 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -596,8 +596,11 @@ public:
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
};
-const DummySignatureChecker DUMMY_CHECKER;
+}
+
+const BaseSignatureChecker& DUMMY_CHECKER = DummySignatureChecker();
+namespace {
class DummySignatureCreator final : public BaseSignatureCreator {
private:
char m_r_len = 32;
diff --git a/src/script/sign.h b/src/script/sign.h
index 958d673b9f..813dfe04e3 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -52,6 +52,8 @@ public:
bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
};
+/** A signature checker that accepts all signatures */
+extern const BaseSignatureChecker& DUMMY_CHECKER;
/** A signature creator that just produces 71-byte empty signatures. */
extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR;
/** A signature creator that just produces 72-byte empty signatures. */
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 1a182209b8..2798e998af 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -101,7 +101,7 @@ bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex,
CBlockHeader header = block->GetBlockHeader();
BlockValidationState state;
- if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, &pindex)) {
+ if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, true, state, &pindex)) {
return false;
}
}
@@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
uint256 chainA_last_header = last_header;
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
}
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
@@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
uint256 chainB_last_header = last_header;
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
}
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
@@ -227,7 +227,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// Reorg back to chain A.
for (size_t i = 2; i < 4; i++) {
const auto& block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
}
// Check that chain A and B blocks can be retrieved.
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index c93d05a93b..132c4e53e7 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -102,7 +102,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
LOCK(cs_main);
BlockValidationState state;
BOOST_CHECK(CheckBlock(block, state, params.GetConsensus()));
- BOOST_CHECK(chainstate.AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr));
+ BOOST_CHECK(chainstate.AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr, true));
CCoinsViewCache view(&chainstate.CoinsTip());
BOOST_CHECK(chainstate.ConnectBlock(block, state, new_block_index, view));
}
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index d0ad037228..6b2ef74e19 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -126,7 +126,7 @@ std::set<std::pair<CPubKey, KeyOriginInfo>> GetKeyOriginData(const FlatSigningPr
return ret;
}
-void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
+void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false)
{
FlatSigningProvider keys_priv, keys_pub;
@@ -340,29 +340,29 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
-void Check(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
+void Check(const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
{
bool found_apostrophes_in_prv = false;
bool found_apostrophes_in_pub = false;
// Do not replace apostrophes with 'h' in prv and pub
- DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, paths);
+ DoCheck(prv, pub, norm_pub, flags, scripts, type, paths);
// Replace apostrophes with 'h' in prv but not in pub, if apostrophes are found in prv
if (prv.find('\'') != std::string::npos) {
found_apostrophes_in_prv = true;
- DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
+ DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
}
// Replace apostrophes with 'h' in pub but not in prv, if apostrophes are found in pub
if (pub.find('\'') != std::string::npos) {
found_apostrophes_in_pub = true;
- DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
+ DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
}
// Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both
if (found_apostrophes_in_prv && found_apostrophes_in_pub) {
- DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
+ DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
}
}
@@ -373,60 +373,61 @@ BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(descriptor_test)
{
// Basic single-key compressed
- Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt);
- Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt);
- Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}});
- Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32);
- Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT);
- Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M);
+ Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt);
+ Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt);
+ Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}});
+ Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32);
+ Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT);
+ Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M);
CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "wpkh(): Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey
CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin
CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin
// Basic single-key uncompressed
- Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt);
- Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt);
- Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY);
+ Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt);
+ Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt);
+ Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY);
CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness
CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "pk(): Uncompressed keys are not allowed"); // No uncompressed keys in witness
CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness
// Some unconventional single-key constructions
- Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}}, OutputType::LEGACY);
- Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY);
- Check("wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa"}}, OutputType::BECH32);
- Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}}, OutputType::BECH32);
- Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}}, OutputType::P2SH_SEGWIT);
- Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT);
- Check("tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", XONLY_KEYS | SIGNABLE | MISSING_PRIVKEYS, {{"51201497ae16f30dacb88523ed9301bff17773b609e8a90518a3f96ea328a47d1500"}}, OutputType::BECH32M);
+ Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}}, OutputType::LEGACY);
+ Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY);
+ Check("wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa"}}, OutputType::BECH32);
+ Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}}, OutputType::BECH32);
+ Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}}, OutputType::P2SH_SEGWIT);
+ Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT);
+ Check("tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", XONLY_KEYS | SIGNABLE | MISSING_PRIVKEYS, {{"51201497ae16f30dacb88523ed9301bff17773b609e8a90518a3f96ea328a47d1500"}}, OutputType::BECH32M);
// Versions with BIP32 derivations
- Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt);
- Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, {{0}});
- Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0)", "pkh([bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}});
- Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
- Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
- Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}});
- Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}});
+ Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt);
+ Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, {{0}});
+ Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}});
+
+ Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
+ Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
+ Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}});
+ Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}});
// Mixed xpubs and const pubkeys
- Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32,{{0},{}});
+ Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32,{{0},{}});
// Mixed range xpubs and const pubkeys
- Check("multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)","multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", RANGE | MIXED_PUBKEYS, {{"512102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e0762103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ec2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121035d30b6c66dc1e036c45369da8287518cf7e0d6ed1e2b905171c605708f14ca032103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"}}, std::nullopt,{{2},{1},{0},{}});
+ Check("multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", RANGE | MIXED_PUBKEYS, {{"512102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e0762103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ec2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121035d30b6c66dc1e036c45369da8287518cf7e0d6ed1e2b905171c605708f14ca032103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"}}, std::nullopt,{{2},{1},{0},{}});
CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo(): Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "pkh(): Key path value 2147483648 is out of range"); // BIP 32 path element overflow
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "pkh(): Key path value '1aa' is not a valid uint32"); // Path is not valid uint
- Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0)", "pkh([01234567/10/20/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}});
+ Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}});
// Multisig constructions
- Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
- Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
- Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
- Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
- Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
- Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT);
- Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M);
+ Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
+ Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
+ Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
+ Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
+ Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT);
+ Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M);
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: Multiple ']' characters found for a single pubkey"); // Double key origin descriptor
CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint
@@ -438,8 +439,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "P2SH script is too large, 581 bytes is larger than 520 bytes"); // Cannot have more than 15 keys in a P2SH multisig, or we exceed maximum push size
- Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32); // In P2WSH we can have up to 20 keys
-Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT); // Even if it's wrapped into P2SH
+ Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32); // In P2WSH we can have up to 20 keys
+ Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT); // Even if it's wrapped into P2SH
// Check for invalid nesting of structures
CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key
CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have combo() at top level"); // Old must be top level
@@ -450,8 +451,8 @@ Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGN
CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have wsh() at top level or inside sh()"); // Cannot embed P2WSH inside P2WSH
// Checksums
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum
@@ -467,7 +468,6 @@ Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGN
Check(
"rawtr(xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/86'/1'/0'/1/*)#a5gn3t7k",
"rawtr(xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/86'/1'/0'/1/*)#4ur3xhft",
- "",
"rawtr([5a61ff8e/86'/1'/0']xpub6DtZpc9PRL2B6pwoNGysmHAaBofDmWv5S6KQEKKGPKhf5fV62ywDtSziSApYVK3JnYY5KUSgiCwiXW5wtd8z7LNBxT9Mu5sEro8itdGfTeA/1/*)#llheyd9x",
RANGE | HARDENED | XONLY_KEYS,
{{"51205172af752f057d543ce8e4a6f8dcf15548ec6be44041bfa93b72e191cfc8c1ee"}, {"51201b66f20b86f700c945ecb9ad9b0ad1662b73084e2bfea48bee02126350b8a5b1"}, {"512063e70f66d815218abcc2306aa930aaca07c5cde73b75127eb27b5e8c16b58a25"}},
@@ -478,7 +478,6 @@ Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGN
Check(
"rawtr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)",
"rawtr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)",
- "rawtr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)",
"rawtr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)",
SIGNABLE | XONLY_KEYS,
{{"5120a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd"}},
@@ -529,9 +528,9 @@ Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGN
CheckUnparsable("wsh(and_b(and_b(older(1),a:older(100000000)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))", "wsh(and_b(and_b(older(1),a:older(100000000)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))", "and_b(older(1),a:older(100000000)) is not sane: contains mixes of timelocks expressed in blocks and seconds");
CheckUnparsable("wsh(and_b(or_b(pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),s:pk(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))", "wsh(and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))", "and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)) is not sane: contains duplicate public keys");
// Valid with extended keys.
- Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", UNSOLVABLE, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, {{},{0}});
+ Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", UNSOLVABLE, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, {{},{0}});
// Valid under sh(wsh()) and with a mix of xpubs and raw keys
- Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", UNSOLVABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, {{},{0}});
+ Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", UNSOLVABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, {{},{0}});
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/bitdeque.cpp b/src/test/fuzz/bitdeque.cpp
new file mode 100644
index 0000000000..01af8320b5
--- /dev/null
+++ b/src/test/fuzz/bitdeque.cpp
@@ -0,0 +1,542 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <util/bitdeque.h>
+
+#include <random.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/util.h>
+
+#include <deque>
+#include <vector>
+
+namespace {
+
+constexpr int LEN_BITS = 16;
+constexpr int RANDDATA_BITS = 20;
+
+using bitdeque_type = bitdeque<128>;
+
+//! Deterministic random vector of bools, for begin/end insertions to draw from.
+std::vector<bool> RANDDATA;
+
+void InitRandData()
+{
+ FastRandomContext ctx(true);
+ RANDDATA.clear();
+ for (size_t i = 0; i < (1U << RANDDATA_BITS) + (1U << LEN_BITS); ++i) {
+ RANDDATA.push_back(ctx.randbool());
+ }
+}
+
+} // namespace
+
+FUZZ_TARGET_INIT(bitdeque, InitRandData)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ FastRandomContext ctx(true);
+
+ size_t maxlen = (1U << provider.ConsumeIntegralInRange<size_t>(0, LEN_BITS)) - 1;
+ size_t limitlen = 4 * maxlen;
+
+ std::deque<bool> deq;
+ bitdeque_type bitdeq;
+
+ const auto& cdeq = deq;
+ const auto& cbitdeq = bitdeq;
+
+ size_t initlen = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ while (initlen) {
+ bool val = ctx.randbool();
+ deq.push_back(val);
+ bitdeq.push_back(val);
+ --initlen;
+ }
+
+ while (provider.remaining_bytes()) {
+ {
+ assert(deq.size() == bitdeq.size());
+ auto it = deq.begin();
+ auto bitit = bitdeq.begin();
+ auto itend = deq.end();
+ while (it != itend) {
+ assert(*it == *bitit);
+ ++it;
+ ++bitit;
+ }
+ }
+
+ CallOneOf(provider,
+ [&] {
+ // constructor()
+ deq = std::deque<bool>{};
+ bitdeq = bitdeque_type{};
+ },
+ [&] {
+ // clear()
+ deq.clear();
+ bitdeq.clear();
+ },
+ [&] {
+ // resize()
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ deq.resize(count);
+ bitdeq.resize(count);
+ },
+ [&] {
+ // assign(count, val)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ deq.assign(count, val);
+ bitdeq.assign(count, val);
+ },
+ [&] {
+ // constructor(count, val)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ deq = std::deque<bool>(count, val);
+ bitdeq = bitdeque_type(count, val);
+ },
+ [&] {
+ // constructor(count)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ deq = std::deque<bool>(count);
+ bitdeq = bitdeque_type(count);
+ },
+ [&] {
+ // construct(begin, end)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ deq = std::deque<bool>(rand_begin, rand_end);
+ bitdeq = bitdeque_type(rand_begin, rand_end);
+ },
+ [&] {
+ // assign(begin, end)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ deq.assign(rand_begin, rand_end);
+ bitdeq.assign(rand_begin, rand_end);
+ },
+ [&] {
+ // construct(initializer_list)
+ std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool(), ctx.randbool(), ctx.randbool()};
+ deq = std::deque<bool>(ilist);
+ bitdeq = bitdeque_type(ilist);
+ },
+ [&] {
+ // assign(initializer_list)
+ std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool()};
+ deq.assign(ilist);
+ bitdeq.assign(ilist);
+ },
+ [&] {
+ // operator=(const&)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ const std::deque<bool> deq2(count, val);
+ deq = deq2;
+ const bitdeque_type bitdeq2(count, val);
+ bitdeq = bitdeq2;
+ },
+ [&] {
+ // operator=(&&)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ std::deque<bool> deq2(count, val);
+ deq = std::move(deq2);
+ bitdeque_type bitdeq2(count, val);
+ bitdeq = std::move(bitdeq2);
+ },
+ [&] {
+ // deque swap
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ std::deque<bool> deq2(rand_begin, rand_end);
+ bitdeque_type bitdeq2(rand_begin, rand_end);
+ using std::swap;
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ swap(deq, deq2);
+ swap(bitdeq, bitdeq2);
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ },
+ [&] {
+ // deque.swap
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ std::deque<bool> deq2(rand_begin, rand_end);
+ bitdeque_type bitdeq2(rand_begin, rand_end);
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ deq.swap(deq2);
+ bitdeq.swap(bitdeq2);
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ },
+ [&] {
+ // operator=(initializer_list)
+ std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool()};
+ deq = ilist;
+ bitdeq = ilist;
+ },
+ [&] {
+ // iterator arithmetic
+ auto pos1 = provider.ConsumeIntegralInRange<long>(0, cdeq.size());
+ auto pos2 = provider.ConsumeIntegralInRange<long>(0, cdeq.size());
+ auto it = deq.begin() + pos1;
+ auto bitit = bitdeq.begin() + pos1;
+ if ((size_t)pos1 != cdeq.size()) assert(*it == *bitit);
+ assert(it - deq.begin() == pos1);
+ assert(bitit - bitdeq.begin() == pos1);
+ if (provider.ConsumeBool()) {
+ it += pos2 - pos1;
+ bitit += pos2 - pos1;
+ } else {
+ it -= pos1 - pos2;
+ bitit -= pos1 - pos2;
+ }
+ if ((size_t)pos2 != cdeq.size()) assert(*it == *bitit);
+ assert(deq.end() - it == bitdeq.end() - bitit);
+ if (provider.ConsumeBool()) {
+ if ((size_t)pos2 != cdeq.size()) {
+ ++it;
+ ++bitit;
+ }
+ } else {
+ if (pos2 != 0) {
+ --it;
+ --bitit;
+ }
+ }
+ assert(deq.end() - it == bitdeq.end() - bitit);
+ },
+ [&] {
+ // begin() and end()
+ assert(deq.end() - deq.begin() == bitdeq.end() - bitdeq.begin());
+ },
+ [&] {
+ // begin() and end() (const)
+ assert(cdeq.end() - cdeq.begin() == cbitdeq.end() - cbitdeq.begin());
+ },
+ [&] {
+ // rbegin() and rend()
+ assert(deq.rend() - deq.rbegin() == bitdeq.rend() - bitdeq.rbegin());
+ },
+ [&] {
+ // rbegin() and rend() (const)
+ assert(cdeq.rend() - cdeq.rbegin() == cbitdeq.rend() - cbitdeq.rbegin());
+ },
+ [&] {
+ // cbegin() and cend()
+ assert(cdeq.cend() - cdeq.cbegin() == cbitdeq.cend() - cbitdeq.cbegin());
+ },
+ [&] {
+ // crbegin() and crend()
+ assert(cdeq.crend() - cdeq.crbegin() == cbitdeq.crend() - cbitdeq.crbegin());
+ },
+ [&] {
+ // size() and maxsize()
+ assert(cdeq.size() == cbitdeq.size());
+ assert(cbitdeq.size() <= cbitdeq.max_size());
+ },
+ [&] {
+ // empty
+ assert(cdeq.empty() == cbitdeq.empty());
+ },
+ [&] {
+ // at (in range) and flip
+ if (!cdeq.empty()) {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ auto& ref = deq.at(pos);
+ auto bitref = bitdeq.at(pos);
+ assert(ref == bitref);
+ if (ctx.randbool()) {
+ ref = !ref;
+ bitref.flip();
+ }
+ }
+ },
+ [&] {
+ // at (maybe out of range) and bit assign
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() + maxlen);
+ bool newval = ctx.randbool();
+ bool throw_deq{false}, throw_bitdeq{false};
+ bool val_deq{false}, val_bitdeq{false};
+ try {
+ auto& ref = deq.at(pos);
+ val_deq = ref;
+ ref = newval;
+ } catch (const std::out_of_range&) {
+ throw_deq = true;
+ }
+ try {
+ auto ref = bitdeq.at(pos);
+ val_bitdeq = ref;
+ ref = newval;
+ } catch (const std::out_of_range&) {
+ throw_bitdeq = true;
+ }
+ assert(throw_deq == throw_bitdeq);
+ assert(throw_bitdeq == (pos >= cdeq.size()));
+ if (!throw_deq) assert(val_deq == val_bitdeq);
+ },
+ [&] {
+ // at (maybe out of range) (const)
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() + maxlen);
+ bool throw_deq{false}, throw_bitdeq{false};
+ bool val_deq{false}, val_bitdeq{false};
+ try {
+ auto& ref = cdeq.at(pos);
+ val_deq = ref;
+ } catch (const std::out_of_range&) {
+ throw_deq = true;
+ }
+ try {
+ auto ref = cbitdeq.at(pos);
+ val_bitdeq = ref;
+ } catch (const std::out_of_range&) {
+ throw_bitdeq = true;
+ }
+ assert(throw_deq == throw_bitdeq);
+ assert(throw_bitdeq == (pos >= cdeq.size()));
+ if (!throw_deq) assert(val_deq == val_bitdeq);
+ },
+ [&] {
+ // operator[]
+ if (!cdeq.empty()) {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ assert(deq[pos] == bitdeq[pos]);
+ if (ctx.randbool()) {
+ deq[pos] = !deq[pos];
+ bitdeq[pos].flip();
+ }
+ }
+ },
+ [&] {
+ // operator[] const
+ if (!cdeq.empty()) {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ assert(deq[pos] == bitdeq[pos]);
+ }
+ },
+ [&] {
+ // front()
+ if (!cdeq.empty()) {
+ auto& ref = deq.front();
+ auto bitref = bitdeq.front();
+ assert(ref == bitref);
+ if (ctx.randbool()) {
+ ref = !ref;
+ bitref = !bitref;
+ }
+ }
+ },
+ [&] {
+ // front() const
+ if (!cdeq.empty()) {
+ auto& ref = cdeq.front();
+ auto bitref = cbitdeq.front();
+ assert(ref == bitref);
+ }
+ },
+ [&] {
+ // back() and swap(bool, ref)
+ if (!cdeq.empty()) {
+ auto& ref = deq.back();
+ auto bitref = bitdeq.back();
+ assert(ref == bitref);
+ if (ctx.randbool()) {
+ ref = !ref;
+ bitref.flip();
+ }
+ }
+ },
+ [&] {
+ // back() const
+ if (!cdeq.empty()) {
+ const auto& cdeq = deq;
+ const auto& cbitdeq = bitdeq;
+ auto& ref = cdeq.back();
+ auto bitref = cbitdeq.back();
+ assert(ref == bitref);
+ }
+ },
+ [&] {
+ // push_back()
+ if (cdeq.size() < limitlen) {
+ bool val = ctx.randbool();
+ if (cdeq.empty()) {
+ deq.push_back(val);
+ bitdeq.push_back(val);
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.push_back(val);
+ bitdeq.push_back(val);
+ assert(ref == bitref); // references are not invalidated
+ }
+ }
+ },
+ [&] {
+ // push_front()
+ if (cdeq.size() < limitlen) {
+ bool val = ctx.randbool();
+ if (cdeq.empty()) {
+ deq.push_front(val);
+ bitdeq.push_front(val);
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.push_front(val);
+ bitdeq.push_front(val);
+ assert(ref == bitref); // references are not invalidated
+ }
+ }
+ },
+ [&] {
+ // pop_back()
+ if (!cdeq.empty()) {
+ if (cdeq.size() == 1) {
+ deq.pop_back();
+ bitdeq.pop_back();
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 2);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.pop_back();
+ bitdeq.pop_back();
+ assert(ref == bitref); // references to other elements are not invalidated
+ }
+ }
+ },
+ [&] {
+ // pop_front()
+ if (!cdeq.empty()) {
+ if (cdeq.size() == 1) {
+ deq.pop_front();
+ bitdeq.pop_front();
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(1, cdeq.size() - 1);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.pop_front();
+ bitdeq.pop_front();
+ assert(ref == bitref); // references to other elements are not invalidated
+ }
+ }
+ },
+ [&] {
+ // erase (in middle, single)
+ if (!cdeq.empty()) {
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ size_t after = cdeq.size() - 1 - before;
+ auto it = deq.erase(cdeq.begin() + before);
+ auto bitit = bitdeq.erase(cbitdeq.begin() + before);
+ assert(it == cdeq.begin() + before && it == cdeq.end() - after);
+ assert(bitit == cbitdeq.begin() + before && bitit == cbitdeq.end() - after);
+ }
+ },
+ [&] {
+ // erase (at front, range)
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ auto it = deq.erase(cdeq.begin(), cdeq.begin() + count);
+ auto bitit = bitdeq.erase(cbitdeq.begin(), cbitdeq.begin() + count);
+ assert(it == deq.begin());
+ assert(bitit == bitdeq.begin());
+ },
+ [&] {
+ // erase (at back, range)
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ auto it = deq.erase(cdeq.end() - count, cdeq.end());
+ auto bitit = bitdeq.erase(cbitdeq.end() - count, cbitdeq.end());
+ assert(it == deq.end());
+ assert(bitit == bitdeq.end());
+ },
+ [&] {
+ // erase (in middle, range)
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - count);
+ size_t after = cdeq.size() - count - before;
+ auto it = deq.erase(cdeq.begin() + before, cdeq.end() - after);
+ auto bitit = bitdeq.erase(cbitdeq.begin() + before, cbitdeq.end() - after);
+ assert(it == cdeq.begin() + before && it == cdeq.end() - after);
+ assert(bitit == cbitdeq.begin() + before && bitit == cbitdeq.end() - after);
+ },
+ [&] {
+ // insert/emplace (in middle, single)
+ if (cdeq.size() < limitlen) {
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ bool val = ctx.randbool();
+ bool do_emplace = provider.ConsumeBool();
+ auto it = deq.insert(cdeq.begin() + before, val);
+ auto bitit = do_emplace ? bitdeq.emplace(cbitdeq.begin() + before, val)
+ : bitdeq.insert(cbitdeq.begin() + before, val);
+ assert(it == deq.begin() + before);
+ assert(bitit == bitdeq.begin() + before);
+ }
+ },
+ [&] {
+ // insert (at front, begin/end)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ auto it = deq.insert(cdeq.begin(), rand_begin, rand_end);
+ auto bitit = bitdeq.insert(cbitdeq.begin(), rand_begin, rand_end);
+ assert(it == cdeq.begin());
+ assert(bitit == cbitdeq.begin());
+ }
+ },
+ [&] {
+ // insert (at back, begin/end)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ auto it = deq.insert(cdeq.end(), rand_begin, rand_end);
+ auto bitit = bitdeq.insert(cbitdeq.end(), rand_begin, rand_end);
+ assert(it == cdeq.end() - count);
+ assert(bitit == cbitdeq.end() - count);
+ }
+ },
+ [&] {
+ // insert (in middle, range)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ bool val = ctx.randbool();
+ auto it = deq.insert(cdeq.begin() + before, count, val);
+ auto bitit = bitdeq.insert(cbitdeq.begin() + before, count, val);
+ assert(it == deq.begin() + before);
+ assert(bitit == bitdeq.begin() + before);
+ }
+ },
+ [&] {
+ // insert (in middle, begin/end)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ auto it = deq.insert(cdeq.begin() + before, rand_begin, rand_end);
+ auto bitit = bitdeq.insert(cbitdeq.begin() + before, rand_begin, rand_end);
+ assert(it == deq.begin() + before);
+ assert(bitit == bitdeq.begin() + before);
+ }
+ }
+ );
+ }
+
+}
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index 0004d82d66..507ce57ec0 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -83,3 +83,40 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
}
}
}
+
+
+FUZZ_TARGET_INIT(pow_transition, initialize_pow)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const Consensus::Params& consensus_params{Params().GetConsensus()};
+ std::vector<std::unique_ptr<CBlockIndex>> blocks;
+
+ const uint32_t old_time{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ const uint32_t new_time{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ const int32_t version{fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ uint32_t nbits{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+
+ const arith_uint256 pow_limit = UintToArith256(consensus_params.powLimit);
+ arith_uint256 old_target;
+ old_target.SetCompact(nbits);
+ if (old_target > pow_limit) {
+ nbits = pow_limit.GetCompact();
+ }
+ // Create one difficulty adjustment period worth of headers
+ for (int height = 0; height < consensus_params.DifficultyAdjustmentInterval(); ++height) {
+ CBlockHeader header;
+ header.nVersion = version;
+ header.nTime = old_time;
+ header.nBits = nbits;
+ if (height == consensus_params.DifficultyAdjustmentInterval() - 1) {
+ header.nTime = new_time;
+ }
+ auto current_block{std::make_unique<CBlockIndex>(header)};
+ current_block->pprev = blocks.empty() ? nullptr : blocks.back().get();
+ current_block->nHeight = height;
+ blocks.emplace_back(std::move(current_block)).get();
+ }
+ auto last_block{blocks.back().get()};
+ unsigned int new_nbits{GetNextWorkRequired(last_block, nullptr, consensus_params)};
+ Assert(PermittedDifficultyTransition(consensus_params, last_block->nHeight + 1, last_block->nBits, new_nbits));
+}
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index 0b596492be..8abb943266 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -58,7 +58,7 @@ FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
if (fuzzed_data_provider.ConsumeBool()) {
for (const auto& block : *g_chain) {
BlockValidationState dummy;
- bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy)};
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)};
Assert(processed);
const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
Assert(index);
diff --git a/src/test/headers_sync_chainwork_tests.cpp b/src/test/headers_sync_chainwork_tests.cpp
new file mode 100644
index 0000000000..41241ebee2
--- /dev/null
+++ b/src/test/headers_sync_chainwork_tests.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chain.h>
+#include <chainparams.h>
+#include <consensus/params.h>
+#include <headerssync.h>
+#include <pow.h>
+#include <test/util/setup_common.h>
+#include <validation.h>
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
+
+struct HeadersGeneratorSetup : public RegTestingSetup {
+ /** Search for a nonce to meet (regtest) proof of work */
+ void FindProofOfWork(CBlockHeader& starting_header);
+ /**
+ * Generate headers in a chain that build off a given starting hash, using
+ * the given nVersion, advancing time by 1 second from the starting
+ * prev_time, and with a fixed merkle root hash.
+ */
+ void GenerateHeaders(std::vector<CBlockHeader>& headers, size_t count,
+ const uint256& starting_hash, const int nVersion, int prev_time,
+ const uint256& merkle_root, const uint32_t nBits);
+};
+
+void HeadersGeneratorSetup::FindProofOfWork(CBlockHeader& starting_header)
+{
+ while (!CheckProofOfWork(starting_header.GetHash(), starting_header.nBits, Params().GetConsensus())) {
+ ++(starting_header.nNonce);
+ }
+}
+
+void HeadersGeneratorSetup::GenerateHeaders(std::vector<CBlockHeader>& headers,
+ size_t count, const uint256& starting_hash, const int nVersion, int prev_time,
+ const uint256& merkle_root, const uint32_t nBits)
+{
+ uint256 prev_hash = starting_hash;
+
+ while (headers.size() < count) {
+ headers.push_back(CBlockHeader());
+ CBlockHeader& next_header = headers.back();;
+ next_header.nVersion = nVersion;
+ next_header.hashPrevBlock = prev_hash;
+ next_header.hashMerkleRoot = merkle_root;
+ next_header.nTime = prev_time+1;
+ next_header.nBits = nBits;
+
+ FindProofOfWork(next_header);
+ prev_hash = next_header.GetHash();
+ prev_time = next_header.nTime;
+ }
+ return;
+}
+
+BOOST_FIXTURE_TEST_SUITE(headers_sync_chainwork_tests, HeadersGeneratorSetup)
+
+// In this test, we construct two sets of headers from genesis, one with
+// sufficient proof of work and one without.
+// 1. We deliver the first set of headers and verify that the headers sync state
+// updates to the REDOWNLOAD phase successfully.
+// 2. Then we deliver the second set of headers and verify that they fail
+// processing (presumably due to commitments not matching).
+// 3. Finally, we verify that repeating with the first set of headers in both
+// phases is successful.
+BOOST_AUTO_TEST_CASE(headers_sync_state)
+{
+ std::vector<CBlockHeader> first_chain;
+ std::vector<CBlockHeader> second_chain;
+
+ std::unique_ptr<HeadersSyncState> hss;
+
+ const int target_blocks = 15000;
+ arith_uint256 chain_work = target_blocks*2;
+
+ // Generate headers for two different chains (using differing merkle roots
+ // to ensure the headers are different).
+ GenerateHeaders(first_chain, target_blocks-1, Params().GenesisBlock().GetHash(),
+ Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
+ ArithToUint256(0), Params().GenesisBlock().nBits);
+
+ GenerateHeaders(second_chain, target_blocks-2, Params().GenesisBlock().GetHash(),
+ Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
+ ArithToUint256(1), Params().GenesisBlock().nBits);
+
+ const CBlockIndex* chain_start = WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(Params().GenesisBlock().GetHash()));
+ std::vector<CBlockHeader> headers_batch;
+
+ // Feed the first chain to HeadersSyncState, by delivering 1 header
+ // initially and then the rest.
+ headers_batch.insert(headers_batch.end(), std::next(first_chain.begin()), first_chain.end());
+
+ hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
+ (void)hss->ProcessNextHeaders({first_chain.front()}, true);
+ // Pretend the first header is still "full", so we don't abort.
+ auto result = hss->ProcessNextHeaders(headers_batch, true);
+
+ // This chain should look valid, and we should have met the proof-of-work
+ // requirement.
+ BOOST_CHECK(result.success);
+ BOOST_CHECK(result.request_more);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
+
+ // Try to sneakily feed back the second chain.
+ result = hss->ProcessNextHeaders(second_chain, true);
+ BOOST_CHECK(!result.success); // foiled!
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
+
+ // Now try again, this time feeding the first chain twice.
+ hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
+ (void)hss->ProcessNextHeaders(first_chain, true);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
+
+ result = hss->ProcessNextHeaders(first_chain, true);
+ BOOST_CHECK(result.success);
+ BOOST_CHECK(!result.request_more);
+ // All headers should be ready for acceptance:
+ BOOST_CHECK(result.pow_validated_headers.size() == first_chain.size());
+ // Nothing left for the sync logic to do:
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
+
+ // Finally, verify that just trying to process the second chain would not
+ // succeed (too little work)
+ hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
+ // Pretend just the first message is "full", so we don't abort.
+ (void)hss->ProcessNextHeaders({second_chain.front()}, true);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
+
+ headers_batch.clear();
+ headers_batch.insert(headers_batch.end(), std::next(second_chain.begin(), 1), second_chain.end());
+ // Tell the sync logic that the headers message was not full, implying no
+ // more headers can be requested. For a low-work-chain, this should causes
+ // the sync to end with no headers for acceptance.
+ result = hss->ProcessNextHeaders(headers_batch, false);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
+ BOOST_CHECK(result.pow_validated_headers.empty());
+ BOOST_CHECK(!result.request_more);
+ // Nevertheless, no validation errors should have been detected with the
+ // chain:
+ BOOST_CHECK(result.success);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp
index bd9ba4b8f7..80684ac205 100644
--- a/src/test/i2p_tests.cpp
+++ b/src/test/i2p_tests.cpp
@@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", CService{}, &interrupt);
{
- ASSERT_DEBUG_LOG("Creating SAM session");
+ ASSERT_DEBUG_LOG("Creating persistent SAM session");
ASSERT_DEBUG_LOG("too many bytes without a terminator");
i2p::Connection conn;
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index d88aed7d4a..9f5fb17b60 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -588,7 +588,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->nNonce = bi.nonce;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, true, nullptr));
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 2f43ae52f7..3695ea9d16 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -20,7 +20,14 @@ BOOST_AUTO_TEST_CASE(get_next_work)
pindexLast.nHeight = 32255;
pindexLast.nTime = 1262152739; // Block #32255
pindexLast.nBits = 0x1d00ffff;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00d86aU);
+
+ // Here (and below): expected_nbits is calculated in
+ // CalculateNextWorkRequired(); redoing the calculation here would be just
+ // reimplementing the same code that is written in pow.cpp. Rather than
+ // copy that code, we just hardcode the expected result.
+ unsigned int expected_nbits = 0x1d00d86aU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
}
/* Test the constraint on the upper bound for next work */
@@ -32,7 +39,9 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
pindexLast.nHeight = 2015;
pindexLast.nTime = 1233061996; // Block #2015
pindexLast.nBits = 0x1d00ffff;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00ffffU);
+ unsigned int expected_nbits = 0x1d00ffffU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
}
/* Test the constraint on the lower bound for actual time taken */
@@ -44,7 +53,12 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
pindexLast.nHeight = 68543;
pindexLast.nTime = 1279297671; // Block #68543
pindexLast.nBits = 0x1c05a3f4;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1c0168fdU);
+ unsigned int expected_nbits = 0x1c0168fdU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
+ // Test that reducing nbits further would not be a PermittedDifficultyTransition.
+ unsigned int invalid_nbits = expected_nbits-1;
+ BOOST_CHECK(!PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, invalid_nbits));
}
/* Test the constraint on the upper bound for actual time taken */
@@ -56,7 +70,12 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
pindexLast.nHeight = 46367;
pindexLast.nTime = 1269211443; // Block #46367
pindexLast.nBits = 0x1c387f6f;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00e1fdU);
+ unsigned int expected_nbits = 0x1d00e1fdU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
+ // Test that increasing nbits further would not be a PermittedDifficultyTransition.
+ unsigned int invalid_nbits = expected_nbits+1;
+ BOOST_CHECK(!PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, invalid_nbits));
}
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 3d3fd5d93d..9f5e3ab7ae 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
for (int n=0; n<100; n++) {
int r = InsecureRandRange(150000);
CBlockIndex* tip = (r < 100000) ? &vBlocksMain[r] : &vBlocksSide[r - 100000];
- CBlockLocator locator = chain.GetLocator(tip);
+ CBlockLocator locator = GetLocator(tip);
// The first result must be the block itself, the last one must be genesis.
BOOST_CHECK(locator.vHave.front() == tip->GetBlockHash());
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 88cf9647e7..faa0b2878c 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -68,7 +68,7 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
assert(block->nNonce);
}
- bool processed{Assert(node.chainman)->ProcessNewBlock(block, true, nullptr)};
+ bool processed{Assert(node.chainman)->ProcessNewBlock(block, true, true, nullptr)};
assert(processed);
return CTxIn{block->vtx[0]->GetHash(), 0};
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 30d26ecf79..8a0b03c9d1 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -321,7 +321,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(
const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr);
+ Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, true, nullptr);
return block;
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 70e2f89e0b..0f9f332dc6 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -23,6 +23,7 @@
#include <util/string.h>
#include <util/time.h>
#include <util/vector.h>
+#include <util/bitdeque.h>
#include <array>
#include <fstream>
@@ -238,15 +239,15 @@ BOOST_AUTO_TEST_CASE(span_write_bytes)
BOOST_AUTO_TEST_CASE(util_Join)
{
// Normal version
- BOOST_CHECK_EQUAL(Join({}, ", "), "");
- BOOST_CHECK_EQUAL(Join({"foo"}, ", "), "foo");
- BOOST_CHECK_EQUAL(Join({"foo", "bar"}, ", "), "foo, bar");
+ BOOST_CHECK_EQUAL(Join(std::vector<std::string>{}, ", "), "");
+ BOOST_CHECK_EQUAL(Join(std::vector<std::string>{"foo"}, ", "), "foo");
+ BOOST_CHECK_EQUAL(Join(std::vector<std::string>{"foo", "bar"}, ", "), "foo, bar");
// Version with unary operator
const auto op_upper = [](const std::string& s) { return ToUpper(s); };
- BOOST_CHECK_EQUAL(Join<std::string>({}, ", ", op_upper), "");
- BOOST_CHECK_EQUAL(Join<std::string>({"foo"}, ", ", op_upper), "FOO");
- BOOST_CHECK_EQUAL(Join<std::string>({"foo", "bar"}, ", ", op_upper), "FOO, BAR");
+ BOOST_CHECK_EQUAL(Join(std::list<std::string>{}, ", ", op_upper), "");
+ BOOST_CHECK_EQUAL(Join(std::list<std::string>{"foo"}, ", ", op_upper), "FOO");
+ BOOST_CHECK_EQUAL(Join(std::list<std::string>{"foo", "bar"}, ", ", op_upper), "FOO, BAR");
}
BOOST_AUTO_TEST_CASE(util_ReplaceAll)
@@ -2510,13 +2511,13 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector)
auto v2 = Vector(std::move(t2));
BOOST_CHECK_EQUAL(v2.size(), 1U);
- BOOST_CHECK(v2[0].origin == &t2);
+ BOOST_CHECK(v2[0].origin == &t2); // NOLINT(*-use-after-move)
BOOST_CHECK_EQUAL(v2[0].copies, 0);
auto v3 = Vector(t1, std::move(t2));
BOOST_CHECK_EQUAL(v3.size(), 2U);
BOOST_CHECK(v3[0].origin == &t1);
- BOOST_CHECK(v3[1].origin == &t2);
+ BOOST_CHECK(v3[1].origin == &t2); // NOLINT(*-use-after-move)
BOOST_CHECK_EQUAL(v3[0].copies, 1);
BOOST_CHECK_EQUAL(v3[1].copies, 0);
@@ -2524,7 +2525,7 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector)
BOOST_CHECK_EQUAL(v4.size(), 3U);
BOOST_CHECK(v4[0].origin == &t1);
BOOST_CHECK(v4[1].origin == &t2);
- BOOST_CHECK(v4[2].origin == &t3);
+ BOOST_CHECK(v4[2].origin == &t3); // NOLINT(*-use-after-move)
BOOST_CHECK_EQUAL(v4[0].copies, 1);
BOOST_CHECK_EQUAL(v4[1].copies, 1);
BOOST_CHECK_EQUAL(v4[2].copies, 0);
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 1c5ca18759..bb1ade153a 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -100,7 +100,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock>
// submit block header, so that miner can get the block height from the
// global state and the node has the topology of the chain
BlockValidationState ignored;
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, ignored));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, true, ignored));
return pblock;
}
@@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
bool ignored;
// Connect the genesis block and drain any outstanding events
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, true, &ignored));
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
@@ -179,13 +179,13 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
FastRandomContext insecure;
for (int i = 0; i < 1000; i++) {
auto block = blocks[insecure.randrange(blocks.size() - 1)];
- Assert(m_node.chainman)->ProcessNewBlock(block, true, &ignored);
+ Assert(m_node.chainman)->ProcessNewBlock(block, true, true, &ignored);
}
// to make sure that eventually we process the full chain - do it here
for (const auto& block : blocks) {
if (block->vtx.size() == 1) {
- bool processed = Assert(m_node.chainman)->ProcessNewBlock(block, true, &ignored);
+ bool processed = Assert(m_node.chainman)->ProcessNewBlock(block, true, true, &ignored);
assert(processed);
}
}
@@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
bool ignored;
auto ProcessBlock = [&](std::shared_ptr<const CBlock> block) -> bool {
- return Assert(m_node.chainman)->ProcessNewBlock(block, /*force_processing=*/true, /*new_block=*/&ignored);
+ return Assert(m_node.chainman)->ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&ignored);
};
// Process all mined blocks
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index ee60f9aa4d..98294b9028 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -132,7 +132,7 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
BOOST_CHECK(checked);
bool accepted = background_cs.AcceptBlock(
- pblock, state, &pindex, true, nullptr, &newblock);
+ pblock, state, &pindex, true, nullptr, &newblock, true);
BOOST_CHECK(accepted);
}
// UpdateTip is called here
diff --git a/src/timedata.cpp b/src/timedata.cpp
index ceee08e68c..fe9a5fbed7 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -32,9 +32,9 @@ int64_t GetTimeOffset()
return nTimeOffset;
}
-int64_t GetAdjustedTime()
+NodeClock::time_point GetAdjustedTime()
{
- return GetTime() + GetTimeOffset();
+ return NodeClock::now() + std::chrono::seconds{GetTimeOffset()};
}
#define BITCOIN_TIMEDATA_MAX_SAMPLES 200
diff --git a/src/timedata.h b/src/timedata.h
index cfe5111644..669a571f47 100644
--- a/src/timedata.h
+++ b/src/timedata.h
@@ -75,7 +75,7 @@ public:
/** Functions to keep track of adjusted P2P time */
int64_t GetTimeOffset();
-int64_t GetAdjustedTime();
+NodeClock::time_point GetAdjustedTime();
void AddTimeData(const CNetAddr& ip, int64_t nTime);
/**
diff --git a/src/txmempool.h b/src/txmempool.h
index 73904cc370..d06816ba97 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -365,7 +365,7 @@ enum class MemPoolRemovalReason {
* - a transaction which doesn't meet the minimum fee requirements.
* - a new transaction that double-spends an input of a transaction already in
* the pool where the new transaction does not meet the Replace-By-Fee
- * requirements as defined in BIP 125.
+ * requirements as defined in doc/policy/mempool-replacements.md.
* - a non-standard transaction.
*
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
diff --git a/src/util/bitdeque.h b/src/util/bitdeque.h
new file mode 100644
index 0000000000..1e34b72475
--- /dev/null
+++ b/src/util/bitdeque.h
@@ -0,0 +1,469 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_BITDEQUE_H
+#define BITCOIN_UTIL_BITDEQUE_H
+
+#include <bitset>
+#include <cstddef>
+#include <deque>
+#include <limits>
+#include <stdexcept>
+#include <tuple>
+
+/** Class that mimics std::deque<bool>, but with std::vector<bool>'s bit packing.
+ *
+ * BlobSize selects the (minimum) number of bits that are allocated at once.
+ * Larger values reduce the asymptotic memory usage overhead, at the cost of
+ * needing larger up-front allocations. The default is 4096 bytes.
+ */
+template<int BlobSize = 4096 * 8>
+class bitdeque
+{
+ // Internal definitions
+ using word_type = std::bitset<BlobSize>;
+ using deque_type = std::deque<word_type>;
+ static_assert(BlobSize > 0);
+ static constexpr int BITS_PER_WORD = BlobSize;
+
+ // Forward and friend declarations of iterator types.
+ template<bool Const> class Iterator;
+ template<bool Const> friend class Iterator;
+
+ /** Iterator to a bitdeque element, const or not. */
+ template<bool Const>
+ class Iterator
+ {
+ using deque_iterator = std::conditional_t<Const, typename deque_type::const_iterator, typename deque_type::iterator>;
+
+ deque_iterator m_it;
+ int m_bitpos{0};
+ Iterator(const deque_iterator& it, int bitpos) : m_it(it), m_bitpos(bitpos) {}
+ friend class bitdeque;
+
+ public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = bool;
+ using pointer = void;
+ using const_pointer = void;
+ using reference = std::conditional_t<Const, bool, typename word_type::reference>;
+ using const_reference = bool;
+ using difference_type = std::ptrdiff_t;
+
+ /** Default constructor. */
+ Iterator() = default;
+
+ /** Default copy constructor. */
+ Iterator(const Iterator&) = default;
+
+ /** Conversion from non-const to const iterator. */
+ template<bool ConstArg = Const, typename = std::enable_if_t<Const && ConstArg>>
+ Iterator(const Iterator<false>& x) : m_it(x.m_it), m_bitpos(x.m_bitpos) {}
+
+ Iterator& operator+=(difference_type dist)
+ {
+ if (dist > 0) {
+ if (dist + m_bitpos >= BITS_PER_WORD) {
+ ++m_it;
+ dist -= BITS_PER_WORD - m_bitpos;
+ m_bitpos = 0;
+ }
+ auto jump = dist / BITS_PER_WORD;
+ m_it += jump;
+ m_bitpos += dist - jump * BITS_PER_WORD;
+ } else if (dist < 0) {
+ dist = -dist;
+ if (dist > m_bitpos) {
+ --m_it;
+ dist -= m_bitpos + 1;
+ m_bitpos = BITS_PER_WORD - 1;
+ }
+ auto jump = dist / BITS_PER_WORD;
+ m_it -= jump;
+ m_bitpos -= dist - jump * BITS_PER_WORD;
+ }
+ return *this;
+ }
+
+ friend difference_type operator-(const Iterator& x, const Iterator& y)
+ {
+ return BITS_PER_WORD * (x.m_it - y.m_it) + x.m_bitpos - y.m_bitpos;
+ }
+
+ Iterator& operator=(const Iterator&) = default;
+ Iterator& operator-=(difference_type dist) { return operator+=(-dist); }
+ Iterator& operator++() { ++m_bitpos; if (m_bitpos == BITS_PER_WORD) { m_bitpos = 0; ++m_it; }; return *this; }
+ Iterator operator++(int) { auto ret{*this}; operator++(); return ret; }
+ Iterator& operator--() { if (m_bitpos == 0) { m_bitpos = BITS_PER_WORD; --m_it; }; --m_bitpos; return *this; }
+ Iterator operator--(int) { auto ret{*this}; operator--(); return ret; }
+ friend Iterator operator+(Iterator x, difference_type dist) { x += dist; return x; }
+ friend Iterator operator+(difference_type dist, Iterator x) { x += dist; return x; }
+ friend Iterator operator-(Iterator x, difference_type dist) { x -= dist; return x; }
+ friend bool operator<(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) < std::tie(y.m_it, y.m_bitpos); }
+ friend bool operator>(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) > std::tie(y.m_it, y.m_bitpos); }
+ friend bool operator<=(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) <= std::tie(y.m_it, y.m_bitpos); }
+ friend bool operator>=(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) >= std::tie(y.m_it, y.m_bitpos); }
+ friend bool operator==(const Iterator& x, const Iterator& y) { return x.m_it == y.m_it && x.m_bitpos == y.m_bitpos; }
+ friend bool operator!=(const Iterator& x, const Iterator& y) { return x.m_it != y.m_it || x.m_bitpos != y.m_bitpos; }
+ reference operator*() const { return (*m_it)[m_bitpos]; }
+ reference operator[](difference_type pos) const { return *(*this + pos); }
+ };
+
+public:
+ using value_type = bool;
+ using size_type = std::size_t;
+ using difference_type = typename deque_type::difference_type;
+ using reference = typename word_type::reference;
+ using const_reference = bool;
+ using iterator = Iterator<false>;
+ using const_iterator = Iterator<true>;
+ using pointer = void;
+ using const_pointer = void;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+private:
+ /** Deque of bitsets storing the actual bit data. */
+ deque_type m_deque;
+
+ /** Number of unused bits at the front of m_deque.front(). */
+ int m_pad_begin;
+
+ /** Number of unused bits at the back of m_deque.back(). */
+ int m_pad_end;
+
+ /** Shrink the container by n bits, removing from the end. */
+ void erase_back(size_type n)
+ {
+ if (n >= static_cast<size_type>(BITS_PER_WORD - m_pad_end)) {
+ n -= BITS_PER_WORD - m_pad_end;
+ m_pad_end = 0;
+ m_deque.erase(m_deque.end() - 1 - (n / BITS_PER_WORD), m_deque.end());
+ n %= BITS_PER_WORD;
+ }
+ if (n) {
+ auto& last = m_deque.back();
+ while (n) {
+ last.reset(BITS_PER_WORD - 1 - m_pad_end);
+ ++m_pad_end;
+ --n;
+ }
+ }
+ }
+
+ /** Extend the container by n bits, adding at the end. */
+ void extend_back(size_type n)
+ {
+ if (n > static_cast<size_type>(m_pad_end)) {
+ n -= m_pad_end + 1;
+ m_pad_end = BITS_PER_WORD - 1;
+ m_deque.insert(m_deque.end(), 1 + (n / BITS_PER_WORD), {});
+ n %= BITS_PER_WORD;
+ }
+ m_pad_end -= n;
+ }
+
+ /** Shrink the container by n bits, removing from the beginning. */
+ void erase_front(size_type n)
+ {
+ if (n >= static_cast<size_type>(BITS_PER_WORD - m_pad_begin)) {
+ n -= BITS_PER_WORD - m_pad_begin;
+ m_pad_begin = 0;
+ m_deque.erase(m_deque.begin(), m_deque.begin() + 1 + (n / BITS_PER_WORD));
+ n %= BITS_PER_WORD;
+ }
+ if (n) {
+ auto& first = m_deque.front();
+ while (n) {
+ first.reset(m_pad_begin);
+ ++m_pad_begin;
+ --n;
+ }
+ }
+ }
+
+ /** Extend the container by n bits, adding at the beginning. */
+ void extend_front(size_type n)
+ {
+ if (n > static_cast<size_type>(m_pad_begin)) {
+ n -= m_pad_begin + 1;
+ m_pad_begin = BITS_PER_WORD - 1;
+ m_deque.insert(m_deque.begin(), 1 + (n / BITS_PER_WORD), {});
+ n %= BITS_PER_WORD;
+ }
+ m_pad_begin -= n;
+ }
+
+ /** Insert a sequence of falses anywhere in the container. */
+ void insert_zeroes(size_type before, size_type count)
+ {
+ size_type after = size() - before;
+ if (before < after) {
+ extend_front(count);
+ std::move(begin() + count, begin() + count + before, begin());
+ } else {
+ extend_back(count);
+ std::move_backward(begin() + before, begin() + before + after, end());
+ }
+ }
+
+public:
+ /** Construct an empty container. */
+ explicit bitdeque() : m_pad_begin{0}, m_pad_end{0} {}
+
+ /** Set the container equal to count times the value of val. */
+ void assign(size_type count, bool val)
+ {
+ m_deque.clear();
+ m_deque.resize((count + BITS_PER_WORD - 1) / BITS_PER_WORD);
+ m_pad_begin = 0;
+ m_pad_end = 0;
+ if (val) {
+ for (auto& elem : m_deque) elem.flip();
+ }
+ if (count % BITS_PER_WORD) {
+ erase_back(BITS_PER_WORD - (count % BITS_PER_WORD));
+ }
+ }
+
+ /** Construct a container containing count times the value of val. */
+ bitdeque(size_type count, bool val) { assign(count, val); }
+
+ /** Construct a container containing count false values. */
+ explicit bitdeque(size_t count) { assign(count, false); }
+
+ /** Copy constructor. */
+ bitdeque(const bitdeque&) = default;
+
+ /** Move constructor. */
+ bitdeque(bitdeque&&) noexcept = default;
+
+ /** Copy assignment operator. */
+ bitdeque& operator=(const bitdeque& other) = default;
+
+ /** Move assignment operator. */
+ bitdeque& operator=(bitdeque&& other) noexcept = default;
+
+ // Iterator functions.
+ iterator begin() noexcept { return {m_deque.begin(), m_pad_begin}; }
+ iterator end() noexcept { return iterator{m_deque.end(), 0} - m_pad_end; }
+ const_iterator begin() const noexcept { return const_iterator{m_deque.cbegin(), m_pad_begin}; }
+ const_iterator cbegin() const noexcept { return const_iterator{m_deque.cbegin(), m_pad_begin}; }
+ const_iterator end() const noexcept { return const_iterator{m_deque.cend(), 0} - m_pad_end; }
+ const_iterator cend() const noexcept { return const_iterator{m_deque.cend(), 0} - m_pad_end; }
+ reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
+ reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
+ const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{cend()}; }
+ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
+ const_reverse_iterator rend() const noexcept { return const_reverse_iterator{cbegin()}; }
+ const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
+
+ /** Count the number of bits in the container. */
+ size_type size() const noexcept { return m_deque.size() * BITS_PER_WORD - m_pad_begin - m_pad_end; }
+
+ /** Determine whether the container is empty. */
+ bool empty() const noexcept
+ {
+ return m_deque.size() == 0 || (m_deque.size() == 1 && (m_pad_begin + m_pad_end == BITS_PER_WORD));
+ }
+
+ /** Return the maximum size of the container. */
+ size_type max_size() const noexcept
+ {
+ if (m_deque.max_size() < std::numeric_limits<difference_type>::max() / BITS_PER_WORD) {
+ return m_deque.max_size() * BITS_PER_WORD;
+ } else {
+ return std::numeric_limits<difference_type>::max();
+ }
+ }
+
+ /** Set the container equal to the bits in [first,last). */
+ template<typename It>
+ void assign(It first, It last)
+ {
+ size_type count = std::distance(first, last);
+ assign(count, false);
+ auto it = begin();
+ while (first != last) {
+ *(it++) = *(first++);
+ }
+ }
+
+ /** Set the container equal to the bits in ilist. */
+ void assign(std::initializer_list<bool> ilist)
+ {
+ assign(ilist.size(), false);
+ auto it = begin();
+ auto init = ilist.begin();
+ while (init != ilist.end()) {
+ *(it++) = *(init++);
+ }
+ }
+
+ /** Set the container equal to the bits in ilist. */
+ bitdeque& operator=(std::initializer_list<bool> ilist)
+ {
+ assign(ilist);
+ return *this;
+ }
+
+ /** Construct a container containing the bits in [first,last). */
+ template<typename It>
+ bitdeque(It first, It last) { assign(first, last); }
+
+ /** Construct a container containing the bits in ilist. */
+ bitdeque(std::initializer_list<bool> ilist) { assign(ilist); }
+
+ // Access an element of the container, with bounds checking.
+ reference at(size_type position)
+ {
+ if (position >= size()) throw std::out_of_range("bitdeque::at() out of range");
+ return begin()[position];
+ }
+ const_reference at(size_type position) const
+ {
+ if (position >= size()) throw std::out_of_range("bitdeque::at() out of range");
+ return cbegin()[position];
+ }
+
+ // Access elements of the container without bounds checking.
+ reference operator[](size_type position) { return begin()[position]; }
+ const_reference operator[](size_type position) const { return cbegin()[position]; }
+ reference front() { return *begin(); }
+ const_reference front() const { return *cbegin(); }
+ reference back() { return end()[-1]; }
+ const_reference back() const { return cend()[-1]; }
+
+ /** Release unused memory. */
+ void shrink_to_fit()
+ {
+ m_deque.shrink_to_fit();
+ }
+
+ /** Empty the container. */
+ void clear() noexcept
+ {
+ m_deque.clear();
+ m_pad_begin = m_pad_end = 0;
+ }
+
+ // Append an element to the container.
+ void push_back(bool val)
+ {
+ extend_back(1);
+ back() = val;
+ }
+ reference emplace_back(bool val)
+ {
+ extend_back(1);
+ auto ref = back();
+ ref = val;
+ return ref;
+ }
+
+ // Prepend an element to the container.
+ void push_front(bool val)
+ {
+ extend_front(1);
+ front() = val;
+ }
+ reference emplace_front(bool val)
+ {
+ extend_front(1);
+ auto ref = front();
+ ref = val;
+ return ref;
+ }
+
+ // Remove the last element from the container.
+ void pop_back()
+ {
+ erase_back(1);
+ }
+
+ // Remove the first element from the container.
+ void pop_front()
+ {
+ erase_front(1);
+ }
+
+ /** Resize the container. */
+ void resize(size_type n)
+ {
+ if (n < size()) {
+ erase_back(size() - n);
+ } else {
+ extend_back(n - size());
+ }
+ }
+
+ // Swap two containers.
+ void swap(bitdeque& other) noexcept
+ {
+ std::swap(m_deque, other.m_deque);
+ std::swap(m_pad_begin, other.m_pad_begin);
+ std::swap(m_pad_end, other.m_pad_end);
+ }
+ friend void swap(bitdeque& b1, bitdeque& b2) noexcept { b1.swap(b2); }
+
+ // Erase elements from the container.
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ size_type before = std::distance(cbegin(), first);
+ size_type dist = std::distance(first, last);
+ size_type after = std::distance(last, cend());
+ if (before < after) {
+ std::move_backward(begin(), begin() + before, end() - after);
+ erase_front(dist);
+ return begin() + before;
+ } else {
+ std::move(end() - after, end(), begin() + before);
+ erase_back(dist);
+ return end() - after;
+ }
+ }
+
+ iterator erase(iterator first, iterator last) { return erase(const_iterator{first}, const_iterator{last}); }
+ iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
+ iterator erase(iterator pos) { return erase(const_iterator{pos}, const_iterator{pos} + 1); }
+
+ // Insert elements into the container.
+ iterator insert(const_iterator pos, bool val)
+ {
+ size_type before = pos - cbegin();
+ insert_zeroes(before, 1);
+ auto it = begin() + before;
+ *it = val;
+ return it;
+ }
+
+ iterator emplace(const_iterator pos, bool val) { return insert(pos, val); }
+
+ iterator insert(const_iterator pos, size_type count, bool val)
+ {
+ size_type before = pos - cbegin();
+ insert_zeroes(before, count);
+ auto it_begin = begin() + before;
+ auto it = it_begin;
+ auto it_end = it + count;
+ while (it != it_end) *(it++) = val;
+ return it_begin;
+ }
+
+ template<typename It>
+ iterator insert(const_iterator pos, It first, It last)
+ {
+ size_type before = pos - cbegin();
+ size_type count = std::distance(first, last);
+ insert_zeroes(before, count);
+ auto it_begin = begin() + before;
+ auto it = it_begin;
+ while (first != last) {
+ *(it++) = *(first++);
+ }
+ return it_begin;
+ }
+};
+
+#endif // BITCOIN_UTIL_BITDEQUE_H
diff --git a/src/util/string.h b/src/util/string.h
index 6d68677464..9b4c9a7e28 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -57,34 +57,30 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin
}
/**
- * Join a list of items
+ * Join all container items. Typically used to concatenate strings but accepts
+ * containers with elements of any type.
*
- * @param list The list to join
- * @param separator The separator
- * @param unary_op Apply this operator to each item in the list
+ * @param container The items to join
+ * @param separator The separator
+ * @param unary_op Apply this operator to each item
*/
-template <typename T, typename BaseType, typename UnaryOp>
-auto Join(const std::vector<T>& list, const BaseType& separator, UnaryOp unary_op)
- -> decltype(unary_op(list.at(0)))
+template <typename C, typename S, typename UnaryOp>
+auto Join(const C& container, const S& separator, UnaryOp unary_op)
{
- decltype(unary_op(list.at(0))) ret;
- for (size_t i = 0; i < list.size(); ++i) {
- if (i > 0) ret += separator;
- ret += unary_op(list.at(i));
+ decltype(unary_op(*container.begin())) ret;
+ bool first{true};
+ for (const auto& item : container) {
+ if (!first) ret += separator;
+ ret += unary_op(item);
+ first = false;
}
return ret;
}
-template <typename T, typename T2>
-T Join(const std::vector<T>& list, const T2& separator)
+template <typename C, typename S>
+auto Join(const C& container, const S& separator)
{
- return Join(list, separator, [](const T& i) { return i; });
-}
-
-// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above.
-inline std::string Join(const std::vector<std::string>& list, std::string_view separator)
-{
- return Join<std::string>(list, separator);
+ return Join(container, separator, [](const auto& i) { return i; });
}
/**
diff --git a/src/validation.cpp b/src/validation.cpp
index d276cea2f7..a2a339a5a1 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -449,7 +449,7 @@ public:
/** Whether we allow transactions to replace mempool transactions by BIP125 rules. If false,
* any transaction spending the same inputs as a transaction in the mempool is considered
* a conflict. */
- const bool m_allow_bip125_replacement;
+ const bool m_allow_replacement;
/** When true, the mempool will not be trimmed when individual transactions are submitted in
* Finalize(). Instead, limits should be enforced at the end to ensure the package is not
* partially submitted.
@@ -469,7 +469,7 @@ public:
/* m_bypass_limits */ bypass_limits,
/* m_coins_to_uncache */ coins_to_uncache,
/* m_test_accept */ test_accept,
- /* m_allow_bip125_replacement */ true,
+ /* m_allow_replacement */ true,
/* m_package_submission */ false,
/* m_package_feerates */ false,
};
@@ -483,7 +483,7 @@ public:
/* m_bypass_limits */ false,
/* m_coins_to_uncache */ coins_to_uncache,
/* m_test_accept */ true,
- /* m_allow_bip125_replacement */ false,
+ /* m_allow_replacement */ false,
/* m_package_submission */ false, // not submitting to mempool
/* m_package_feerates */ false,
};
@@ -497,7 +497,7 @@ public:
/* m_bypass_limits */ false,
/* m_coins_to_uncache */ coins_to_uncache,
/* m_test_accept */ false,
- /* m_allow_bip125_replacement */ false,
+ /* m_allow_replacement */ false,
/* m_package_submission */ true,
/* m_package_feerates */ true,
};
@@ -510,7 +510,7 @@ public:
/* m_bypass_limits */ false,
/* m_coins_to_uncache */ package_args.m_coins_to_uncache,
/* m_test_accept */ package_args.m_test_accept,
- /* m_allow_bip125_replacement */ true,
+ /* m_allow_replacement */ true,
/* m_package_submission */ false,
/* m_package_feerates */ false, // only 1 transaction
};
@@ -524,7 +524,7 @@ public:
bool bypass_limits,
std::vector<COutPoint>& coins_to_uncache,
bool test_accept,
- bool allow_bip125_replacement,
+ bool allow_replacement,
bool package_submission,
bool package_feerates)
: m_chainparams{chainparams},
@@ -532,7 +532,7 @@ public:
m_bypass_limits{bypass_limits},
m_coins_to_uncache{coins_to_uncache},
m_test_accept{test_accept},
- m_allow_bip125_replacement{allow_bip125_replacement},
+ m_allow_replacement{allow_replacement},
m_package_submission{package_submission},
m_package_feerates{package_feerates}
{
@@ -731,7 +731,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
{
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
- if (!args.m_allow_bip125_replacement) {
+ if (!args.m_allow_replacement) {
// Transaction conflicts with a mempool tx, but we're not allowing replacements.
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
}
@@ -861,8 +861,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Specifically, the subset of RBF transactions which we allow despite chain limits are those which
// conflict directly with exactly one other transaction (but may evict children of said transaction),
// and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies"
- // check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is
- // amended, we may need to move that check to here instead of removing it wholesale.
+ // check is accomplished later, so we don't bother doing anything about it here, but if our
+ // policy changes, we may need to move that check to here instead of removing it wholesale.
//
// Such transactions are clearly not merging any existing packages, so we are only concerned with
// ensuring that (a) no package is growing past the package size (not count) limits and (b) we are
@@ -929,7 +929,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
TxValidationState& state = ws.m_state;
CFeeRate newFeeRate(ws.m_modified_fees, ws.m_vsize);
- // The replacement transaction must have a higher feerate than its direct conflicts.
+ // Enforce Rule #6. The replacement transaction must have a higher feerate than its direct conflicts.
// - The motivation for this check is to ensure that the replacement transaction is preferable for
// block-inclusion, compared to what would be removed from the mempool.
// - This logic predates ancestor feerate-based transaction selection, which is why it doesn't
@@ -942,18 +942,18 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
- // Calculate all conflicting entries and enforce BIP125 Rule #5.
+ // Calculate all conflicting entries and enforce Rule #5.
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, ws.m_all_conflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"too many potential replacements", *err_string);
}
- // Enforce BIP125 Rule #2.
+ // Enforce Rule #2.
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, ws.m_iters_conflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"replacement-adds-unconfirmed", *err_string);
}
// Check if it's economically rational to mine this transaction rather than the ones it
- // replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
+ // replaces and pays for its own relay fees. Enforce Rules #3 and #4.
for (CTxMemPool::txiter it : ws.m_all_conflicting) {
ws.m_conflicting_fees += it->GetModifiedFee();
ws.m_conflicting_size += it->GetTxSize();
@@ -1224,7 +1224,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
// package to spend. Since we already checked conflicts in the package and we don't allow
// replacements, we don't need to track the coins spent. Note that this logic will need to be
// updated if package replace-by-fee is allowed in the future.
- assert(!args.m_allow_bip125_replacement);
+ assert(!args.m_allow_replacement);
m_viewmempool.PackageAddTransaction(ws.m_ptx);
}
@@ -2944,7 +2944,7 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
}
// Send block tip changed notifications without cs_main
if (fNotify) {
- uiInterface.NotifyHeaderTip(GetSynchronizationState(fInitialBlockDownload), pindexHeader);
+ uiInterface.NotifyHeaderTip(GetSynchronizationState(fInitialBlockDownload), pindexHeader->nHeight, pindexHeader->nTime, false);
}
return fNotify;
}
@@ -3432,6 +3432,22 @@ std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock&
return commitment;
}
+bool HasValidProofOfWork(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams)
+{
+ return std::all_of(headers.cbegin(), headers.cend(),
+ [&](const auto& header) { return CheckProofOfWork(header.GetHash(), header.nBits, consensusParams);});
+}
+
+arith_uint256 CalculateHeadersWork(const std::vector<CBlockHeader>& headers)
+{
+ arith_uint256 total_work{0};
+ for (const CBlockHeader& header : headers) {
+ CBlockIndex dummy(header);
+ total_work += GetBlockProof(dummy);
+ }
+ return total_work;
+}
+
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
@@ -3441,7 +3457,7 @@ std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock&
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
-static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev, NodeClock::time_point now) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
assert(pindexPrev != nullptr);
@@ -3469,8 +3485,9 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
// Check timestamp
- if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
+ if (block.Time() > now + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME}) {
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
+ }
// Reject blocks with outdated version
if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_HEIGHTINCB)) ||
@@ -3571,9 +3588,10 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
return true;
}
-bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex)
+bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex, bool min_pow_checked)
{
AssertLockHeld(cs_main);
+
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf{m_blockman.m_block_index.find(hash)};
@@ -3607,7 +3625,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
- if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_adjusted_time_callback())) {
+ if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_options.adjusted_time_callback())) {
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -3651,6 +3669,10 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
}
}
+ if (!min_pow_checked) {
+ LogPrint(BCLog::VALIDATION, "%s: not adding new block header %s, missing anti-dos proof-of-work validation\n", __func__, hash.ToString());
+ return state.Invalid(BlockValidationResult::BLOCK_HEADER_LOW_WORK, "too-little-chainwork");
+ }
CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, m_best_header)};
if (ppindex)
@@ -3660,14 +3682,14 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
// Exposed wrapper for AcceptBlockHeader
-bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CBlockIndex** ppindex)
+bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex)
{
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- bool accepted{AcceptBlockHeader(header, state, &pindex)};
+ bool accepted{AcceptBlockHeader(header, state, &pindex, min_pow_checked)};
ActiveChainstate().CheckBlockIndex();
if (!accepted) {
@@ -3689,8 +3711,33 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
return true;
}
+void ChainstateManager::ReportHeadersPresync(const arith_uint256& work, int64_t height, int64_t timestamp)
+{
+ AssertLockNotHeld(cs_main);
+ const auto& chainstate = ActiveChainstate();
+ {
+ LOCK(cs_main);
+ // Don't report headers presync progress if we already have a post-minchainwork header chain.
+ // This means we lose reporting for potentially legimate, but unlikely, deep reorgs, but
+ // prevent attackers that spam low-work headers from filling our logs.
+ if (m_best_header->nChainWork >= UintToArith256(GetConsensus().nMinimumChainWork)) return;
+ // Rate limit headers presync updates to 4 per second, as these are not subject to DoS
+ // protection.
+ auto now = std::chrono::steady_clock::now();
+ if (now < m_last_presync_update + std::chrono::milliseconds{250}) return;
+ m_last_presync_update = now;
+ }
+ bool initial_download = chainstate.IsInitialBlockDownload();
+ uiInterface.NotifyHeaderTip(GetSynchronizationState(initial_download), height, timestamp, /*presync=*/true);
+ if (initial_download) {
+ const int64_t blocks_left{(GetTime() - timestamp) / GetConsensus().nPowTargetSpacing};
+ const double progress{100.0 * height / (height + blocks_left)};
+ LogPrintf("Pre-synchronizing blockheaders, height: %d (~%.2f%%)\n", height, progress);
+ }
+}
+
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
+bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked)
{
const CBlock& block = *pblock;
@@ -3700,7 +3747,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
- bool accepted_header{m_chainman.AcceptBlockHeader(block, state, &pindex)};
+ bool accepted_header{m_chainman.AcceptBlockHeader(block, state, &pindex, min_pow_checked)};
CheckBlockIndex();
if (!accepted_header)
@@ -3773,7 +3820,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
return true;
}
-bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
+bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked, bool* new_block)
{
AssertLockNotHeld(cs_main);
@@ -3794,7 +3841,7 @@ bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& blo
bool ret = CheckBlock(*block, state, GetConsensus());
if (ret) {
// Store to disk
- ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
+ ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block, min_pow_checked);
}
if (!ret) {
GetMainSignals().BlockChecked(*block, state);
@@ -3831,7 +3878,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
- const std::function<int64_t()>& adjusted_time_callback,
+ const std::function<NodeClock::time_point()>& adjusted_time_callback,
bool fCheckPOW,
bool fCheckMerkleRoot)
{
@@ -4331,7 +4378,7 @@ void CChainState::LoadExternalBlockFile(
const CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
BlockValidationState state;
- if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr)) {
+ if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr, true)) {
nLoaded++;
}
if (state.IsError()) {
@@ -4369,7 +4416,7 @@ void CChainState::LoadExternalBlockFile(
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
- if (AcceptBlock(pblockrecursive, dummy, nullptr, true, &it->second, nullptr)) {
+ if (AcceptBlock(pblockrecursive, dummy, nullptr, true, &it->second, nullptr, true)) {
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
}
diff --git a/src/validation.h b/src/validation.h
index 324074aee9..7f5039aaea 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -147,7 +147,7 @@ struct MempoolAcceptResult {
const TxValidationState m_state;
// The following fields are only present when m_result_type = ResultType::VALID or MEMPOOL_ENTRY
- /** Mempool transactions replaced by the tx per BIP 125 rules. */
+ /** Mempool transactions replaced by the tx. */
const std::optional<std::list<CTransactionRef>> m_replaced_transactions;
/** Virtual size as used by the mempool, calculated using serialized size and sigops. */
const std::optional<int64_t> m_vsize;
@@ -336,10 +336,16 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
- const std::function<int64_t()>& adjusted_time_callback,
+ const std::function<NodeClock::time_point()>& adjusted_time_callback,
bool fCheckPOW = true,
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+/** Check with the proof of work on each blockheader matches the value in nBits */
+bool HasValidProofOfWork(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams);
+
+/** Return the sum of the work on a given set of headers */
+arith_uint256 CalculateHeadersWork(const std::vector<CBlockHeader>& headers);
+
/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
class CVerifyDB {
public:
@@ -650,7 +656,7 @@ public:
EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex)
LOCKS_EXCLUDED(::cs_main);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
@@ -838,10 +844,6 @@ private:
CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr};
- const CChainParams m_chainparams;
-
- const std::function<int64_t()> m_adjusted_time_callback;
-
//! Internal helper for ActivateSnapshot().
[[nodiscard]] bool PopulateAndValidateSnapshot(
CChainState& snapshot_chainstate,
@@ -851,22 +853,30 @@ private:
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to m_block_index.
+ * Caller must set min_pow_checked=true in order to add a new header to the
+ * block index (permanent memory storage), indicating that the header is
+ * known to be part of a sufficiently high-work chain (anti-dos check).
*/
bool AcceptBlockHeader(
const CBlockHeader& block,
BlockValidationState& state,
- CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CBlockIndex** ppindex,
+ bool min_pow_checked) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
friend CChainState;
+ /** Most recent headers presync progress update, for rate-limiting. */
+ std::chrono::time_point<std::chrono::steady_clock> m_last_presync_update GUARDED_BY(::cs_main) {};
+
public:
using Options = kernel::ChainstateManagerOpts;
- explicit ChainstateManager(const Options& opts)
- : m_chainparams{opts.chainparams},
- m_adjusted_time_callback{Assert(opts.adjusted_time_callback)} {};
+ explicit ChainstateManager(Options options) : m_options{std::move(options)}
+ {
+ Assert(m_options.adjusted_time_callback);
+ }
- const CChainParams& GetParams() const { return m_chainparams; }
- const Consensus::Params& GetConsensus() const { return m_chainparams.GetConsensus(); }
+ const CChainParams& GetParams() const { return m_options.chainparams; }
+ const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); }
/**
* Alias for ::cs_main.
@@ -881,6 +891,7 @@ public:
*/
RecursiveMutex& GetMutex() const LOCK_RETURNED(::cs_main) { return ::cs_main; }
+ const Options m_options;
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
@@ -991,10 +1002,15 @@ public:
*
* @param[in] block The block we want to process.
* @param[in] force_processing Process this block even if unrequested; used for non-network block sources.
+ * @param[in] min_pow_checked True if proof-of-work anti-DoS checks have
+ * been done by caller for headers chain
+ * (note: only affects headers acceptance; if
+ * block header is already present in block
+ * index then this parameter has no effect)
* @param[out] new_block A boolean which is set to indicate if the block was first received via this call
* @returns If the block was processed, independently of block validity
*/
- bool ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
+ bool ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked, bool* new_block) LOCKS_EXCLUDED(cs_main);
/**
* Process incoming block headers.
@@ -1003,10 +1019,11 @@ public:
* validationinterface callback.
*
* @param[in] block The block headers themselves
+ * @param[in] min_pow_checked True if proof-of-work anti-DoS checks have been done by caller for headers chain
* @param[out] state This may be set to an Error state if any error occurred processing them
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
*/
- bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
+ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
/**
* Try to add a transaction to the memory pool.
@@ -1030,6 +1047,12 @@ public:
/** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */
std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const;
+ /** This is used by net_processing to report pre-synchronization progress of headers, as
+ * headers are not yet fed to validation during that time, but validation is (for now)
+ * responsible for logging and signalling through NotifyHeaderTip, so it needs this
+ * information. */
+ void ReportHeadersPresync(const arith_uint256& work, int64_t height, int64_t timestamp);
+
~ChainstateManager();
};
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 49e6bac462..b568e90998 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -156,7 +156,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
for (const size_t& i : best_selection) {
result.AddInput(utxo_pool.at(i));
}
- result.ComputeAndSetWaste(CAmount{0});
+ result.ComputeAndSetWaste(cost_of_change, cost_of_change, CAmount{0});
assert(best_waste == result.GetWaste());
return result;
@@ -166,6 +166,12 @@ std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& ut
{
SelectionResult result(target_value, SelectionAlgorithm::SRD);
+ // Include change for SRD as we want to avoid making really small change if the selection just
+ // barely meets the target. Just use the lower bound change target instead of the randomly
+ // generated one, since SRD will result in a random change amount anyway; avoid making the
+ // target needlessly large.
+ target_value += CHANGE_LOWER;
+
std::vector<size_t> indexes;
indexes.resize(utxo_pool.size());
std::iota(indexes.begin(), indexes.end(), 0);
@@ -389,20 +395,26 @@ CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost,
return waste;
}
-CAmount GenerateChangeTarget(CAmount payment_value, FastRandomContext& rng)
+CAmount GenerateChangeTarget(const CAmount payment_value, const CAmount change_fee, FastRandomContext& rng)
{
if (payment_value <= CHANGE_LOWER / 2) {
- return CHANGE_LOWER;
+ return change_fee + CHANGE_LOWER;
} else {
// random value between 50ksat and min (payment_value * 2, 1milsat)
const auto upper_bound = std::min(payment_value * 2, CHANGE_UPPER);
- return rng.randrange(upper_bound - CHANGE_LOWER) + CHANGE_LOWER;
+ return change_fee + rng.randrange(upper_bound - CHANGE_LOWER) + CHANGE_LOWER;
}
}
-void SelectionResult::ComputeAndSetWaste(CAmount change_cost)
+void SelectionResult::ComputeAndSetWaste(const CAmount min_viable_change, const CAmount change_cost, const CAmount change_fee)
{
- m_waste = GetSelectionWaste(m_selected_inputs, change_cost, m_target, m_use_effective);
+ const CAmount change = GetChange(min_viable_change, change_fee);
+
+ if (change > 0) {
+ m_waste = GetSelectionWaste(m_selected_inputs, change_cost, m_target, m_use_effective);
+ } else {
+ m_waste = GetSelectionWaste(m_selected_inputs, 0, m_target, m_use_effective);
+ }
}
CAmount SelectionResult::GetWaste() const
@@ -415,6 +427,11 @@ CAmount SelectionResult::GetSelectedValue() const
return std::accumulate(m_selected_inputs.cbegin(), m_selected_inputs.cend(), CAmount{0}, [](CAmount sum, const auto& coin) { return sum + coin.txout.nValue; });
}
+CAmount SelectionResult::GetSelectedEffectiveValue() const
+{
+ return std::accumulate(m_selected_inputs.cbegin(), m_selected_inputs.cend(), CAmount{0}, [](CAmount sum, const auto& coin) { return sum + coin.GetEffectiveValue(); });
+}
+
void SelectionResult::Clear()
{
m_selected_inputs.clear();
@@ -427,6 +444,16 @@ void SelectionResult::AddInput(const OutputGroup& group)
m_use_effective = !group.m_subtract_fee_outputs;
}
+void SelectionResult::Merge(const SelectionResult& other)
+{
+ m_target += other.m_target;
+ m_use_effective |= other.m_use_effective;
+ if (m_algo == SelectionAlgorithm::MANUAL) {
+ m_algo = other.m_algo;
+ }
+ util::insert(m_selected_inputs, other.m_selected_inputs);
+}
+
const std::set<COutput>& SelectionResult::GetInputSet() const
{
return m_selected_inputs;
@@ -464,4 +491,24 @@ std::string GetAlgorithmName(const SelectionAlgorithm algo)
}
assert(false);
}
+
+CAmount SelectionResult::GetChange(const CAmount min_viable_change, const CAmount change_fee) const
+{
+ // change = SUM(inputs) - SUM(outputs) - fees
+ // 1) With SFFO we don't pay any fees
+ // 2) Otherwise we pay all the fees:
+ // - input fees are covered by GetSelectedEffectiveValue()
+ // - non_input_fee is included in m_target
+ // - change_fee
+ const CAmount change = m_use_effective
+ ? GetSelectedEffectiveValue() - m_target - change_fee
+ : GetSelectedValue() - m_target;
+
+ if (change < min_viable_change) {
+ return 0;
+ }
+
+ return change;
+}
+
} // namespace wallet
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index e257d3f7c7..761c2be0b3 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -123,6 +123,10 @@ struct CoinSelectionParams {
/** Mininmum change to target in Knapsack solver: select coins to cover the payment and
* at least this value of change. */
CAmount m_min_change_target{0};
+ /** Minimum amount for creating a change output.
+ * If change budget is smaller than min_change then we forgo creation of change output.
+ */
+ CAmount min_viable_change{0};
/** Cost of creating the change output. */
CAmount m_change_fee{0};
/** Cost of creating the change output + cost of spending the change output in the future. */
@@ -252,6 +256,7 @@ struct OutputGroup
/** Choose a random change target for each transaction to make it harder to fingerprint the Core
* wallet based on the change output values of transactions it creates.
+ * Change target covers at least change fees and adds a random value on top of it.
* The random value is between 50ksat and min(2 * payment_value, 1milsat)
* When payment_value <= 25ksat, the value is just 50ksat.
*
@@ -261,8 +266,9 @@ struct OutputGroup
* coins selected are just sufficient to cover the payment amount ("unnecessary input" heuristic).
*
* @param[in] payment_value Average payment value of the transaction output(s).
+ * @param[in] change_fee Fee for creating a change output.
*/
-[[nodiscard]] CAmount GenerateChangeTarget(CAmount payment_value, FastRandomContext& rng);
+[[nodiscard]] CAmount GenerateChangeTarget(const CAmount payment_value, const CAmount change_fee, FastRandomContext& rng);
enum class SelectionAlgorithm : uint8_t
{
@@ -279,17 +285,16 @@ struct SelectionResult
private:
/** Set of inputs selected by the algorithm to use in the transaction */
std::set<COutput> m_selected_inputs;
+ /** The target the algorithm selected for. Equal to the recipient amount plus non-input fees */
+ CAmount m_target;
+ /** The algorithm used to produce this result */
+ SelectionAlgorithm m_algo;
/** Whether the input values for calculations should be the effective value (true) or normal value (false) */
bool m_use_effective{false};
/** The computed waste */
std::optional<CAmount> m_waste;
public:
- /** The target the algorithm selected for. Note that this may not be equal to the recipient amount as it can include non-input fees */
- const CAmount m_target;
- /** The algorithm used to produce this result */
- const SelectionAlgorithm m_algo;
-
explicit SelectionResult(const CAmount target, SelectionAlgorithm algo)
: m_target(target), m_algo(algo) {}
@@ -298,20 +303,47 @@ public:
/** Get the sum of the input values */
[[nodiscard]] CAmount GetSelectedValue() const;
+ [[nodiscard]] CAmount GetSelectedEffectiveValue() const;
+
void Clear();
void AddInput(const OutputGroup& group);
/** Calculates and stores the waste for this selection via GetSelectionWaste */
- void ComputeAndSetWaste(CAmount change_cost);
+ void ComputeAndSetWaste(const CAmount min_viable_change, const CAmount change_cost, const CAmount change_fee);
[[nodiscard]] CAmount GetWaste() const;
+ void Merge(const SelectionResult& other);
+
/** Get m_selected_inputs */
const std::set<COutput>& GetInputSet() const;
/** Get the vector of COutputs that will be used to fill in a CTransaction's vin */
std::vector<COutput> GetShuffledInputVector() const;
bool operator<(SelectionResult other) const;
+
+ /** Get the amount for the change output after paying needed fees.
+ *
+ * The change amount is not 100% precise due to discrepancies in fee calculation.
+ * The final change amount (if any) should be corrected after calculating the final tx fees.
+ * When there is a discrepancy, most of the time the final change would be slightly bigger than estimated.
+ *
+ * Following are the possible factors of discrepancy:
+ * + non-input fees always include segwit flags
+ * + input fee estimation always include segwit stack size
+ * + input fees are rounded individually and not collectively, which leads to small rounding errors
+ * - input counter size is always assumed to be 1vbyte
+ *
+ * @param[in] min_viable_change Minimum amount for change output, if change would be less then we forgo change
+ * @param[in] change_fee Fees to include change output in the tx
+ * @returns Amount for change output, 0 when there is no change.
+ *
+ */
+ CAmount GetChange(const CAmount min_viable_change, const CAmount change_fee) const;
+
+ CAmount GetTarget() const { return m_target; }
+
+ SelectionAlgorithm GetAlgo() const { return m_algo; }
};
std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change);
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index c2b8082eae..6d7bb299cc 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/validation.h>
#include <interfaces/chain.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -19,7 +20,7 @@
namespace wallet {
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
-static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, bool require_mine, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
if (wallet.HasWalletSpend(wtx.tx)) {
errors.push_back(Untranslated("Transaction has descendants in the wallet"));
@@ -48,20 +49,21 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
return feebumper::Result::WALLET_ERROR;
}
- // check that original tx consists entirely of our inputs
- // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
- isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- if (!AllInputsMine(wallet, *wtx.tx, filter)) {
- errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
- return feebumper::Result::WALLET_ERROR;
+ if (require_mine) {
+ // check that original tx consists entirely of our inputs
+ // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
+ isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
+ if (!AllInputsMine(wallet, *wtx.tx, filter)) {
+ errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
+ return feebumper::Result::WALLET_ERROR;
+ }
}
-
return feebumper::Result::OK;
}
//! Check if the user provided a valid feeRate
-static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector<bilingual_str>& errors)
+static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, CAmount old_fee, std::vector<bilingual_str>& errors)
{
// check that fee rate is higher than mempool's minimum fee
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
@@ -83,8 +85,6 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
CFeeRate incrementalRelayFee = std::max(wallet.chain().relayIncrementalFee(), CFeeRate(WALLET_INCREMENTAL_RELAY_FEE));
// Given old total fee and transaction size, calculate the old feeRate
- isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- CAmount old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
CFeeRate nOldFeeRate(old_fee, txSize);
// Min total fee is old fee + relay fee
@@ -128,8 +128,8 @@ static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, con
// WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
// network wide policy for incremental relay fee that our node may not be
// aware of. This ensures we're over the required relay fee rate
- // (BIP 125 rule 4). The replacement tx will be at least as large as the
- // original tx, so the total fee will be greater (BIP 125 rule 3)
+ // (Rule 4). The replacement tx will be at least as large as the
+ // original tx, so the total fee will be greater (Rule 3)
CFeeRate node_incremental_relay_fee = wallet.chain().relayIncrementalFee();
CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);
@@ -150,12 +150,12 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
if (wtx == nullptr) return false;
std::vector<bilingual_str> errors_dummy;
- feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
+ feebumper::Result res = PreconditionChecks(wallet, *wtx, /* require_mine=*/ true, errors_dummy);
return res == feebumper::Result::OK;
}
Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
- CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine)
{
// We are going to modify coin control later, copy to re-use
CCoinControl new_coin_control(coin_control);
@@ -169,13 +169,63 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
const CWalletTx& wtx = it->second;
- Result result = PreconditionChecks(wallet, wtx, errors);
+ // Retrieve all of the UTXOs and add them to coin control
+ // While we're here, calculate the input amount
+ std::map<COutPoint, Coin> coins;
+ CAmount input_value = 0;
+ std::vector<CTxOut> spent_outputs;
+ for (const CTxIn& txin : wtx.tx->vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ wallet.chain().findCoins(coins);
+ for (const CTxIn& txin : wtx.tx->vin) {
+ const Coin& coin = coins.at(txin.prevout);
+ if (coin.out.IsNull()) {
+ errors.push_back(Untranslated(strprintf("%s:%u is already spent", txin.prevout.hash.GetHex(), txin.prevout.n)));
+ return Result::MISC_ERROR;
+ }
+ if (wallet.IsMine(txin.prevout)) {
+ new_coin_control.Select(txin.prevout);
+ } else {
+ new_coin_control.SelectExternal(txin.prevout, coin.out);
+ }
+ input_value += coin.out.nValue;
+ spent_outputs.push_back(coin.out);
+ }
+
+ // Figure out if we need to compute the input weight, and do so if necessary
+ PrecomputedTransactionData txdata;
+ txdata.Init(*wtx.tx, std::move(spent_outputs), /* force=*/ true);
+ for (unsigned int i = 0; i < wtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = wtx.tx->vin.at(i);
+ const Coin& coin = coins.at(txin.prevout);
+
+ if (new_coin_control.IsExternalSelected(txin.prevout)) {
+ // For external inputs, we estimate the size using the size of this input
+ int64_t input_weight = GetTransactionInputWeight(txin);
+ // Because signatures can have different sizes, we need to figure out all of the
+ // signature sizes and replace them with the max sized signature.
+ // In order to do this, we verify the script with a special SignatureChecker which
+ // will observe the signatures verified and record their sizes.
+ SignatureWeights weights;
+ TransactionSignatureChecker tx_checker(wtx.tx.get(), i, coin.out.nValue, txdata, MissingDataBehavior::FAIL);
+ SignatureWeightChecker size_checker(weights, tx_checker);
+ VerifyScript(txin.scriptSig, coin.out.scriptPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, size_checker);
+ // Add the difference between max and current to input_weight so that it represents the largest the input could be
+ input_weight += weights.GetWeightDiffToMax();
+ new_coin_control.SetInputWeight(txin.prevout, input_weight);
+ }
+ }
+
+ Result result = PreconditionChecks(wallet, wtx, require_mine, errors);
if (result != Result::OK) {
return result;
}
// Fill in recipients(and preserve a single change key if there is one)
+ // While we're here, calculate the output amount
std::vector<CRecipient> recipients;
+ CAmount output_value = 0;
for (const auto& output : wtx.tx->vout) {
if (!OutputIsChange(wallet, output)) {
CRecipient recipient = {output.scriptPubKey, output.nValue, false};
@@ -185,16 +235,22 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
ExtractDestination(output.scriptPubKey, change_dest);
new_coin_control.destChange = change_dest;
}
+ output_value += output.nValue;
}
- isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
+ old_fee = input_value - output_value;
if (coin_control.m_feerate) {
// The user provided a feeRate argument.
// We calculate this here to avoid compiler warning on the cs_wallet lock
- const int64_t maxTxSize{CalculateMaximumSignedTxSize(*wtx.tx, &wallet).vsize};
- Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors);
+ // We need to make a temporary transaction with no input witnesses as the dummy signer expects them to be empty for external inputs
+ CMutableTransaction mtx{*wtx.tx};
+ for (auto& txin : mtx.vin) {
+ txin.scriptSig.clear();
+ txin.scriptWitness.SetNull();
+ }
+ const int64_t maxTxSize{CalculateMaximumSignedTxSize(CTransaction(mtx), &wallet, &new_coin_control).vsize};
+ Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, old_fee, errors);
if (res != Result::OK) {
return res;
}
@@ -254,7 +310,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
const CWalletTx& oldWtx = it->second;
// make sure the transaction still has no descendants and hasn't been mined in the meantime
- Result result = PreconditionChecks(wallet, oldWtx, errors);
+ Result result = PreconditionChecks(wallet, oldWtx, /* require_mine=*/ false, errors);
if (result != Result::OK) {
return result;
}
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 191878a137..760ab58e5c 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_WALLET_FEEBUMPER_H
#define BITCOIN_WALLET_FEEBUMPER_H
+#include <consensus/consensus.h>
+#include <script/interpreter.h>
#include <primitives/transaction.h>
class uint256;
@@ -31,14 +33,25 @@ enum class Result
//! Return whether transaction can be bumped.
bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid);
-//! Create bumpfee transaction based on feerate estimates.
+/** Create bumpfee transaction based on feerate estimates.
+ *
+ * @param[in] wallet The wallet to use for this bumping
+ * @param[in] txid The txid of the transaction to bump
+ * @param[in] coin_control A CCoinControl object which provides feerates and other information used for coin selection
+ * @param[out] errors Errors
+ * @param[out] old_fee The fee the original transaction pays
+ * @param[out] new_fee the fee that the bump transaction pays
+ * @param[out] mtx The bump transaction itself
+ * @param[in] require_mine Whether the original transaction must consist of inputs that can be spent by the wallet
+ */
Result CreateRateBumpTransaction(CWallet& wallet,
const uint256& txid,
const CCoinControl& coin_control,
std::vector<bilingual_str>& errors,
CAmount& old_fee,
CAmount& new_fee,
- CMutableTransaction& mtx);
+ CMutableTransaction& mtx,
+ bool require_mine);
//! Sign the new transaction,
//! @return false if the tx couldn't be found or if it was
@@ -55,6 +68,55 @@ Result CommitTransaction(CWallet& wallet,
std::vector<bilingual_str>& errors,
uint256& bumped_txid);
+struct SignatureWeights
+{
+private:
+ int m_sigs_count{0};
+ int64_t m_sigs_weight{0};
+
+public:
+ void AddSigWeight(const size_t weight, const SigVersion sigversion)
+ {
+ switch (sigversion) {
+ case SigVersion::BASE:
+ m_sigs_weight += weight * WITNESS_SCALE_FACTOR;
+ m_sigs_count += 1 * WITNESS_SCALE_FACTOR;
+ break;
+ case SigVersion::WITNESS_V0:
+ m_sigs_weight += weight;
+ m_sigs_count++;
+ break;
+ case SigVersion::TAPROOT:
+ case SigVersion::TAPSCRIPT:
+ assert(false);
+ }
+ }
+
+ int64_t GetWeightDiffToMax() const
+ {
+ // Note: the witness scaling factor is already accounted for because the count is multiplied by it.
+ return (/* max signature size=*/ 72 * m_sigs_count) - m_sigs_weight;
+ }
+};
+
+class SignatureWeightChecker : public DeferringSignatureChecker
+{
+private:
+ SignatureWeights& m_weights;
+
+public:
+ SignatureWeightChecker(SignatureWeights& weights, const BaseSignatureChecker& checker) : DeferringSignatureChecker(checker), m_weights(weights) {}
+
+ bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& pubkey, const CScript& script, SigVersion sigversion) const override
+ {
+ if (m_checker.CheckECDSASignature(sig, pubkey, script, sigversion)) {
+ m_weights.AddSigWeight(sig.size(), sigversion);
+ return true;
+ }
+ return false;
+ }
+};
+
} // namespace feebumper
} // namespace wallet
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 4fbc519e39..98925e6b10 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -291,7 +291,7 @@ public:
CAmount& new_fee,
CMutableTransaction& mtx) override
{
- return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx) == feebumper::Result::OK;
+ return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx, /* require_mine= */ true) == feebumper::Result::OK;
}
bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(*m_wallet.get(), mtx); }
bool commitBumpTransaction(const uint256& txid,
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index c06513588b..6eb0ef5e7a 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -151,7 +151,7 @@ void StartWallets(WalletContext& context, CScheduler& scheduler)
if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
scheduler.scheduleEvery([&context] { MaybeCompactWalletDB(context); }, std::chrono::milliseconds{500});
}
- scheduler.scheduleEvery([&context] { MaybeResendWalletTxs(context); }, std::chrono::milliseconds{1000});
+ scheduler.scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
}
void FlushWallets(WalletContext& context)
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 306053fd0c..49e5b5d5e7 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -101,7 +101,7 @@ RPCHelpMan importprivkey()
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
- "but the key was used to create transactions, rescanwallet needs to be called with the appropriate block range.\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
@@ -204,7 +204,7 @@ RPCHelpMan importaddress()
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
- "but the key was used to create transactions, rescanwallet needs to be called with the appropriate block range.\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"If you have the full public key, you should call importpubkey instead of this.\n"
"Hint: use importmulti to import more than one address.\n"
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
@@ -406,7 +406,7 @@ RPCHelpMan importpubkey()
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
- "but the key was used to create transactions, rescanwallet needs to be called with the appropriate block range.\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
@@ -1257,7 +1257,7 @@ RPCHelpMan importmulti()
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
"The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
- "but the key was used to create transactions, rescanwallet needs to be called with the appropriate block range.\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index c6e7588fb4..18136c8e25 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -224,7 +224,7 @@ RPCHelpMan sendtoaddress()
"transaction, just kept in your wallet."},
{"subtractfeefromamount", RPCArg::Type::BOOL, RPCArg::Default{false}, "The fee will be deducted from the amount being sent.\n"
"The recipient will receive less bitcoins than you enter in the amount field."},
- {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Signal that this transaction can be replaced by a transaction (BIP 125)"},
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
"\"" + FeeModes("\"\n\"") + "\""},
@@ -333,7 +333,7 @@ RPCHelpMan sendmany()
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
},
- {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Signal that this transaction can be replaced by a transaction (BIP 125)"},
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
"\"" + FeeModes("\"\n\"") + "\""},
@@ -1045,7 +1045,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
CMutableTransaction mtx;
feebumper::Result res;
// Targeting feerate bump.
- res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
+ res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt);
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index a0769d3ecb..0e13e4756b 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -421,8 +421,8 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "comment", /*optional=*/true, "If a comment is associated with the transaction, only present if not empty."},
- {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
- "may be unknown for unconfirmed transactions not in the mempool."},
+ {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability.\n"
+ "May be unknown for unconfirmed transactions not in the mempool because their unconfirmed ancestors are unknown."},
{RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'received'. List of parent descriptors for the scriptPubKey of this coin.", {
{RPCResult::Type::STR, "desc", "The descriptor string."},
}},
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index d9b1eca020..d84310c323 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -502,32 +502,20 @@ std::optional<SelectionResult> ChooseSelectionResult(const CWallet& wallet, cons
// Vector of results. We will choose the best one based on waste.
std::vector<SelectionResult> results;
- // Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
- std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, true /* positive_only */);
+ std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, /*positive_only=*/true);
if (auto bnb_result{SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change)}) {
results.push_back(*bnb_result);
}
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
- std::vector<OutputGroup> all_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, false /* positive_only */);
- CAmount target_with_change = nTargetValue;
- // While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
- // So we need to include that for KnapsackSolver and SRD as well, as we are expecting to create a change output.
- if (!coin_selection_params.m_subtract_fee_outputs) {
- target_with_change += coin_selection_params.m_change_fee;
- }
- if (auto knapsack_result{KnapsackSolver(all_groups, target_with_change, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) {
- knapsack_result->ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
+ std::vector<OutputGroup> all_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, /*positive_only=*/false);
+ if (auto knapsack_result{KnapsackSolver(all_groups, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) {
+ knapsack_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
results.push_back(*knapsack_result);
}
- // Include change for SRD as we want to avoid making really small change if the selection just
- // barely meets the target. Just use the lower bound change target instead of the randomly
- // generated one, since SRD will result in a random change amount anyway; avoid making the
- // target needlessly large.
- const CAmount srd_target = target_with_change + CHANGE_LOWER;
- if (auto srd_result{SelectCoinsSRD(positive_groups, srd_target, coin_selection_params.rng_fast)}) {
- srd_result->ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
+ if (auto srd_result{SelectCoinsSRD(positive_groups, nTargetValue, coin_selection_params.rng_fast)}) {
+ srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
results.push_back(*srd_result);
}
@@ -603,7 +591,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL);
result.AddInput(preset_inputs);
if (result.GetSelectedValue() < nTargetValue) return std::nullopt;
- result.ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
+ result.ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
return result;
}
@@ -628,12 +616,15 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
available_coins.Shuffle(coin_selection_params.rng_fast);
}
+ SelectionResult preselected(preset_inputs.GetSelectionAmount(), SelectionAlgorithm::MANUAL);
+ preselected.AddInput(preset_inputs);
+
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
// transaction at a target feerate. If an attempt fails, more attempts may be made using a more
// permissive CoinEligibilityFilter.
std::optional<SelectionResult> res = [&] {
// Pre-selected inputs already cover the target amount.
- if (value_to_select <= 0) return std::make_optional(SelectionResult(nTargetValue, SelectionAlgorithm::MANUAL));
+ if (value_to_select <= 0) return std::make_optional(SelectionResult(value_to_select, SelectionAlgorithm::MANUAL));
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
// confirmations on outputs received from other wallets and only spend confirmed change.
@@ -687,9 +678,9 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
if (!res) return std::nullopt;
// Add preset inputs to result
- res->AddInput(preset_inputs);
- if (res->m_algo == SelectionAlgorithm::MANUAL) {
- res->ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
+ res->Merge(preselected);
+ if (res->GetAlgo() == SelectionAlgorithm::MANUAL) {
+ res->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
}
return res;
@@ -803,7 +794,6 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
coin_selection_params.m_subtract_fee_outputs = true;
}
}
- coin_selection_params.m_min_change_target = GenerateChangeTarget(std::floor(recipients_sum / vecSend.size()), rng_fast);
// Create change script that will be used if we need change
CScript scriptChange;
@@ -872,6 +862,15 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
+ coin_selection_params.m_min_change_target = GenerateChangeTarget(std::floor(recipients_sum / vecSend.size()), coin_selection_params.m_change_fee, rng_fast);
+
+ // The smallest change amount should be:
+ // 1. at least equal to dust threshold
+ // 2. at least 1 sat greater than fees to spend it at m_discard_feerate
+ const auto dust = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate);
+ const auto change_spend_fee = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size);
+ coin_selection_params.min_viable_change = std::max(change_spend_fee + 1, dust);
+
// vouts to the payees
if (!coin_selection_params.m_subtract_fee_outputs) {
coin_selection_params.tx_noinputs_size = 10; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 witness overhead (dummy, flag, stack size)
@@ -910,25 +909,22 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
if (!result) {
return util::Error{_("Insufficient funds")};
}
- TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->m_algo).c_str(), result->m_target, result->GetWaste(), result->GetSelectedValue());
-
- // Always make a change output
- // We will reduce the fee from this change output later, and remove the output if it is too small.
- const CAmount change_and_fee = result->GetSelectedValue() - recipients_sum;
- assert(change_and_fee >= 0);
- CTxOut newTxOut(change_and_fee, scriptChange);
-
- if (nChangePosInOut == -1) {
- // Insert change txn at random position:
- nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
- }
- else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
- return util::Error{_("Transaction change output index out of range")};
+ TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->GetAlgo()).c_str(), result->GetTarget(), result->GetWaste(), result->GetSelectedValue());
+
+ const CAmount change_amount = result->GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
+ if (change_amount > 0) {
+ CTxOut newTxOut(change_amount, scriptChange);
+ if (nChangePosInOut == -1) {
+ // Insert change txn at random position:
+ nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
+ } else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
+ return util::Error{_("Transaction change output index out of range")};
+ }
+ txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
+ } else {
+ nChangePosInOut = -1;
}
- assert(nChangePosInOut != -1);
- auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
-
// Shuffle selected coins and fill in final vin
std::vector<COutput> selected_coins = result->GetShuffledInputVector();
@@ -952,42 +948,23 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
if (nBytes == -1) {
return util::Error{_("Missing solving data for estimating transaction size")};
}
- nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
-
- // Subtract fee from the change output if not subtracting it from recipient outputs
- CAmount fee_needed = nFeeRet;
- if (!coin_selection_params.m_subtract_fee_outputs) {
- change_position->nValue -= fee_needed;
- }
-
- // We want to drop the change to fees if:
- // 1. The change output would be dust
- // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
- CAmount change_amount = change_position->nValue;
- if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
- {
- nChangePosInOut = -1;
- change_amount = 0;
- txNew.vout.erase(change_position);
-
- // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
- nBytes = tx_sizes.vsize;
- fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
- }
+ CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+ nFeeRet = result->GetSelectedValue() - recipients_sum - change_amount;
- // The only time that fee_needed should be less than the amount available for fees (in change_and_fee - change_amount) is when
+ // The only time that fee_needed should be less than the amount available for fees is when
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
- assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= change_and_fee - change_amount);
+ assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= nFeeRet);
- // Update nFeeRet in case fee_needed changed due to dropping the change output
- if (fee_needed <= change_and_fee - change_amount) {
- nFeeRet = change_and_fee - change_amount;
+ // If there is a change output and we overpay the fees then increase the change to match the fee needed
+ if (nChangePosInOut != -1 && fee_needed < nFeeRet) {
+ auto& change = txNew.vout.at(nChangePosInOut);
+ change.nValue += nFeeRet - fee_needed;
+ nFeeRet = fee_needed;
}
// Reduce output values for subtractFeeFromAmount
if (coin_selection_params.m_subtract_fee_outputs) {
- CAmount to_reduce = fee_needed + change_amount - change_and_fee;
+ CAmount to_reduce = fee_needed - nFeeRet;
int i = 0;
bool fFirst = true;
for (const auto& recipient : vecSend)
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 4dfdc87a38..30a7fb9eb6 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -248,9 +248,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Iteration exhaustion test
CAmount target = make_hard_case(17, utxo_pool);
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0)); // Should exhaust
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 1)); // Should exhaust
target = make_hard_case(14, utxo_pool);
- const auto result7 = SelectCoinsBnB(GroupCoins(utxo_pool), target, 0); // Should not exhaust
+ const auto result7 = SelectCoinsBnB(GroupCoins(utxo_pool), target, 1); // Should not exhaust
BOOST_CHECK(result7);
// Test same value early bailout optimization
@@ -289,8 +289,8 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Make sure that effective value is working in AttemptSelection when BnB is used
CoinSelectionParams coin_selection_params_bnb{
rand,
- /*change_output_size=*/ 0,
- /*change_spend_size=*/ 0,
+ /*change_output_size=*/ 31,
+ /*change_spend_size=*/ 68,
/*min_change_target=*/ 0,
/*effective_feerate=*/ CFeeRate(3000),
/*long_term_feerate=*/ CFeeRate(1000),
@@ -298,6 +298,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
/*tx_noinputs_size=*/ 0,
/*avoid_partial=*/ false,
};
+ coin_selection_params_bnb.m_change_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(coin_selection_params_bnb.change_output_size);
+ coin_selection_params_bnb.m_cost_of_change = coin_selection_params_bnb.m_effective_feerate.GetFee(coin_selection_params_bnb.change_spend_size) + coin_selection_params_bnb.m_change_fee;
+ coin_selection_params_bnb.min_viable_change = coin_selection_params_bnb.m_effective_feerate.GetFee(coin_selection_params_bnb.change_spend_size);
{
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
wallet->LoadWallet();
@@ -777,6 +780,8 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
/*tx_noinputs_size=*/ 0,
/*avoid_partial=*/ false,
};
+ cs_params.m_cost_of_change = 1;
+ cs_params.min_viable_change = 1;
CCoinControl cc;
const auto result = SelectCoins(*wallet, available_coins, target, cc, cs_params);
BOOST_CHECK(result);
diff --git a/src/wallet/test/feebumper_tests.cpp b/src/wallet/test/feebumper_tests.cpp
new file mode 100644
index 0000000000..6add86dc3d
--- /dev/null
+++ b/src/wallet/test/feebumper_tests.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or https://www.opensource.org/licenses/mit-license.php.
+
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <util/strencodings.h>
+#include <wallet/feebumper.h>
+#include <wallet/test/util.h>
+#include <wallet/test/wallet_test_fixture.h>
+
+#include <boost/test/unit_test.hpp>
+
+namespace wallet {
+namespace feebumper {
+BOOST_FIXTURE_TEST_SUITE(feebumper_tests, WalletTestingSetup)
+
+static void CheckMaxWeightComputation(const std::string& script_str, const std::vector<std::string>& witness_str_stack, const std::string& prevout_script_str, int64_t expected_max_weight)
+{
+ std::vector script_data(ParseHex(script_str));
+ CScript script(script_data.begin(), script_data.end());
+ CTxIn input(uint256(), 0, script);
+
+ for (const auto& s : witness_str_stack) {
+ input.scriptWitness.stack.push_back(ParseHex(s));
+ }
+
+ std::vector prevout_script_data(ParseHex(prevout_script_str));
+ CScript prevout_script(prevout_script_data.begin(), prevout_script_data.end());
+
+ int64_t weight = GetTransactionInputWeight(input);
+ SignatureWeights weights;
+ SignatureWeightChecker size_checker(weights, DUMMY_CHECKER);
+ bool script_ok = VerifyScript(input.scriptSig, prevout_script, &input.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, size_checker);
+ BOOST_CHECK(script_ok);
+ weight += weights.GetWeightDiffToMax();
+ BOOST_CHECK_EQUAL(weight, expected_max_weight);
+}
+
+BOOST_AUTO_TEST_CASE(external_max_weight_test)
+{
+ // P2PKH
+ CheckMaxWeightComputation("453042021f03c8957c5ce12940ee6e3333ecc3f633d9a1ac53a55b3ce0351c617fa96abe021f0dccdcce3ef45a63998be9ec748b561baf077b8e862941d0cd5ec08f5afe68012102fccfeb395f0ecd3a77e7bc31c3bc61dc987418b18e395d441057b42ca043f22c", {}, "76a914f60dcfd3392b28adc7662669603641f578eed72d88ac", 593);
+ // P2SH-P2WPKH
+ CheckMaxWeightComputation("160014001dca1b22c599b5a56a87c78417ad2ff39552f1", {"3042021f5443c58eaf45f3e5ef46f8516f966b334a7d497cedda4edb2b9fad57c90c3b021f63a77cb56cde848e2e2dd20b487eec2f53101f634193786083f60b4d23a82301", "026cfe86116f161057deb240201d6b82ebd4f161e0200d63dc9aca65a1d6b38bb7"}, "a9147c8ab5ad7708b97ccb6b483d57aba48ee85214df87", 364);
+ // P2WPKH
+ CheckMaxWeightComputation("", {"3042021f0f8906f0394979d5b737134773e5b88bf036c7d63542301d600ab677ba5a59021f0e9fe07e62c113045fa1c1532e2914720e8854d189c4f5b8c88f57956b704401", "0359edba11ed1a0568094a6296a16c4d5ee4c8cfe2f5e2e6826871b5ecf8188f79"}, "00149961a78658030cc824af4c54fbf5294bec0cabdd", 272);
+ // P2WSH HTLC
+ CheckMaxWeightComputation("", {"3042021f5c4c29e6b686aae5b6d0751e90208592ea96d26bc81d78b0d3871a94a21fa8021f74dc2f971e438ccece8699c8fd15704c41df219ab37b63264f2147d15c34d801", "01", "6321024cf55e52ec8af7866617dc4e7ff8433758e98799906d80e066c6f32033f685f967029000b275210214827893e2dcbe4ad6c20bd743288edad21100404eb7f52ccd6062fd0e7808f268ac"}, "002089e84892873c679b1129edea246e484fd914c2601f776d4f2f4a001eb8059703", 318);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+} // namespace feebumper
+} // namespace wallet
diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp
index 3465f2f331..90a328179e 100644
--- a/src/wallet/test/fuzz/coinselection.cpp
+++ b/src/wallet/test/fuzz/coinselection.cpp
@@ -58,6 +58,8 @@ FUZZ_TARGET(coinselection)
coin_params.m_subtract_fee_outputs = subtract_fee_outputs;
coin_params.m_long_term_feerate = long_term_fee_rate;
coin_params.m_effective_feerate = effective_fee_rate;
+ coin_params.change_output_size = fuzzed_data_provider.ConsumeIntegralInRange<int>(10, 1000);
+ coin_params.m_change_fee = effective_fee_rate.GetFee(coin_params.change_output_size);
// Create some coins
CAmount total_balance{0};
@@ -83,11 +85,11 @@ FUZZ_TARGET(coinselection)
const auto result_bnb = SelectCoinsBnB(group_pos, target, cost_of_change);
auto result_srd = SelectCoinsSRD(group_pos, target, fast_random_context);
- if (result_srd) result_srd->ComputeAndSetWaste(cost_of_change);
+ if (result_srd) result_srd->ComputeAndSetWaste(cost_of_change, cost_of_change, 0);
- CAmount change_target{GenerateChangeTarget(target, fast_random_context)};
+ CAmount change_target{GenerateChangeTarget(target, coin_params.m_change_fee, fast_random_context)};
auto result_knapsack = KnapsackSolver(group_all, target, change_target, fast_random_context);
- if (result_knapsack) result_knapsack->ComputeAndSetWaste(cost_of_change);
+ if (result_knapsack) result_knapsack->ComputeAndSetWaste(cost_of_change, cost_of_change, 0);
// If the total balance is sufficient for the target and we are not using
// effective values, Knapsack should always find a solution.
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 3d989d90f8..69a151d5c8 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -542,6 +542,7 @@ void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in)
LOCK(cs_wallet);
if (nWalletVersion >= nVersion)
return;
+ WalletLogPrintf("Setting minversion to %d\n", nVersion);
nWalletVersion = nVersion;
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 934811dee1..9281045c21 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -93,7 +93,7 @@ static const CAmount DEFAULT_CONSOLIDATE_FEERATE{10000}; // 10 sat/vbyte
static const CAmount DEFAULT_MAX_AVOIDPARTIALSPEND_FEE = 0;
//! discourage APS fee higher than this amount
constexpr CAmount HIGH_APS_FEE{COIN / 10000};
-//! minimum recommended increment for BIP 125 replacement txs
+//! minimum recommended increment for replacement txs
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
@@ -768,7 +768,7 @@ public:
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(const uint256& hashTx);
- /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
+ /** Mark a transaction as replaced by another transaction. */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 462deeae32..850cb8334c 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -1297,7 +1297,7 @@ class FullBlockTest(BitcoinTestFramework):
blocks2 = []
for i in range(89, LARGE_REORG_SIZE + 89):
blocks2.append(self.next_block("alt" + str(i)))
- self.send_blocks(blocks2, False, force_send=True)
+ self.send_blocks(blocks2, False, force_send=False)
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1))
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 2237a4171e..7603248ae5 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -386,7 +386,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_too_many_replacements_with_default_mempool_params(self):
"""
- Test rule 5 of BIP125 (do not allow replacements that cause more than 100
+ Test rule 5 (do not allow replacements that cause more than 100
evictions) without having to rely on non-default mempool parameters.
In order to do this, create a number of "root" UTXOs, and then hang
@@ -405,7 +405,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# limit; 10 works.
num_tx_graphs = 10
- # (Number of transactions per graph, BIP125 rule 5 failure expected)
+ # (Number of transactions per graph, rule 5 failure expected)
cases = [
# Test the base case of evicting fewer than MAX_REPLACEMENT_LIMIT
# transactions.
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 5e50e1ebce..3cbb948e3c 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -615,6 +615,27 @@ class CompactBlocksTest(BitcoinTestFramework):
bad_peer.send_message(msg)
bad_peer.wait_for_disconnect()
+ def test_low_work_compactblocks(self, test_node):
+ # A compactblock with insufficient work won't get its header included
+ node = self.nodes[0]
+ hashPrevBlock = int(node.getblockhash(node.getblockcount() - 150), 16)
+ block = self.build_block_on_tip(node)
+ block.hashPrevBlock = hashPrevBlock
+ block.solve()
+
+ comp_block = HeaderAndShortIDs()
+ comp_block.initialize_from_block(block)
+ with self.nodes[0].assert_debug_log(['[net] Ignoring low-work compact block from peer 0']):
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+
+ tips = node.getchaintips()
+ found = False
+ for x in tips:
+ if x["hash"] == block.hash:
+ found = True
+ break
+ assert not found
+
def test_compactblocks_not_at_tip(self, test_node):
node = self.nodes[0]
# Test that requesting old compactblocks doesn't work.
@@ -833,6 +854,9 @@ class CompactBlocksTest(BitcoinTestFramework):
self.log.info("Testing compactblock requests/announcements not at chain tip...")
self.test_compactblocks_not_at_tip(self.segwit_node)
+ self.log.info("Testing handling of low-work compact blocks...")
+ self.test_low_work_compactblocks(self.segwit_node)
+
self.log.info("Testing handling of incorrect blocktxn responses...")
self.test_incorrect_blocktxn_response(self.segwit_node)
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
index fde1e4bfa2..7e26994511 100755
--- a/test/functional/p2p_dos_header_tree.py
+++ b/test/functional/p2p_dos_header_tree.py
@@ -22,6 +22,7 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.chain = 'testnet3' # Use testnet chain because it has an early checkpoint
self.num_nodes = 2
+ self.extra_args = [["-minimumchainwork=0x0"], ["-minimumchainwork=0x0"]]
def add_options(self, parser):
parser.add_argument(
@@ -62,7 +63,7 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.log.info("Feed all fork headers (succeeds without checkpoint)")
# On node 0 it succeeds because checkpoints are disabled
- self.restart_node(0, extra_args=['-nocheckpoints'])
+ self.restart_node(0, extra_args=['-nocheckpoints', "-minimumchainwork=0x0"])
peer_no_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
peer_no_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
diff --git a/test/functional/p2p_headers_sync_with_minchainwork.py b/test/functional/p2p_headers_sync_with_minchainwork.py
new file mode 100755
index 0000000000..6e17525c28
--- /dev/null
+++ b/test/functional/p2p_headers_sync_with_minchainwork.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019-2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test that we reject low difficulty headers to prevent our block tree from filling up with useless bloat"""
+
+from test_framework.test_framework import BitcoinTestFramework
+
+from test_framework.p2p import (
+ P2PInterface,
+)
+
+from test_framework.messages import (
+ msg_headers,
+)
+
+from test_framework.blocktools import (
+ NORMAL_GBT_REQUEST_PARAMS,
+ create_block,
+)
+
+from test_framework.util import assert_equal
+
+NODE1_BLOCKS_REQUIRED = 15
+NODE2_BLOCKS_REQUIRED = 2047
+
+
+class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+ # Node0 has no required chainwork; node1 requires 15 blocks on top of the genesis block; node2 requires 2047
+ self.extra_args = [["-minimumchainwork=0x0", "-checkblockindex=0"], ["-minimumchainwork=0x1f", "-checkblockindex=0"], ["-minimumchainwork=0x1000", "-checkblockindex=0"]]
+
+ def setup_network(self):
+ self.setup_nodes()
+ self.reconnect_all()
+ self.sync_all()
+
+ def disconnect_all(self):
+ self.disconnect_nodes(0, 1)
+ self.disconnect_nodes(0, 2)
+
+ def reconnect_all(self):
+ self.connect_nodes(0, 1)
+ self.connect_nodes(0, 2)
+
+ def test_chains_sync_when_long_enough(self):
+ self.log.info("Generate blocks on the node with no required chainwork, and verify nodes 1 and 2 have no new headers in their headers tree")
+ with self.nodes[1].assert_debug_log(expected_msgs=["[net] Ignoring low-work chain (height=14)"]), self.nodes[2].assert_debug_log(expected_msgs=["[net] Ignoring low-work chain (height=14)"]):
+ self.generate(self.nodes[0], NODE1_BLOCKS_REQUIRED-1, sync_fun=self.no_op)
+
+ for node in self.nodes[1:]:
+ chaintips = node.getchaintips()
+ assert(len(chaintips) == 1)
+ assert {
+ 'height': 0,
+ 'hash': '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206',
+ 'branchlen': 0,
+ 'status': 'active',
+ } in chaintips
+
+ self.log.info("Generate more blocks to satisfy node1's minchainwork requirement, and verify node2 still has no new headers in headers tree")
+ with self.nodes[2].assert_debug_log(expected_msgs=["[net] Ignoring low-work chain (height=15)"]):
+ self.generate(self.nodes[0], NODE1_BLOCKS_REQUIRED - self.nodes[0].getblockcount(), sync_fun=self.no_op)
+ self.sync_blocks(self.nodes[0:2])
+
+ assert {
+ 'height': 0,
+ 'hash': '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206',
+ 'branchlen': 0,
+ 'status': 'active',
+ } in self.nodes[2].getchaintips()
+
+ assert(len(self.nodes[2].getchaintips()) == 1)
+
+ self.log.info("Generate long chain for node0/node1")
+ self.generate(self.nodes[0], NODE2_BLOCKS_REQUIRED-self.nodes[0].getblockcount(), sync_fun=self.no_op)
+
+ self.log.info("Verify that node2 will sync the chain when it gets long enough")
+ self.sync_blocks()
+
+ def test_peerinfo_includes_headers_presync_height(self):
+ self.log.info("Test that getpeerinfo() includes headers presync height")
+
+ # Disconnect network, so that we can find our own peer connection more
+ # easily
+ self.disconnect_all()
+
+ p2p = self.nodes[0].add_p2p_connection(P2PInterface())
+ node = self.nodes[0]
+
+ # Ensure we have a long chain already
+ current_height = self.nodes[0].getblockcount()
+ if (current_height < 3000):
+ self.generate(node, 3000-current_height, sync_fun=self.no_op)
+
+ # Send a group of 2000 headers, forking from genesis.
+ new_blocks = []
+ hashPrevBlock = int(node.getblockhash(0), 16)
+ for i in range(2000):
+ block = create_block(hashprev = hashPrevBlock, tmpl=node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
+ block.solve()
+ new_blocks.append(block)
+ hashPrevBlock = block.sha256
+
+ headers_message = msg_headers(headers=new_blocks)
+ p2p.send_and_ping(headers_message)
+
+ # getpeerinfo should show a sync in progress
+ assert_equal(node.getpeerinfo()[0]['presynced_headers'], 2000)
+
+ def test_large_reorgs_can_succeed(self):
+ self.log.info("Test that a 2000+ block reorg, starting from a point that is more than 2000 blocks before a locator entry, can succeed")
+
+ self.sync_all() # Ensure all nodes are synced.
+ self.disconnect_all()
+
+ # locator(block at height T) will have heights:
+ # [T, T-1, ..., T-10, T-12, T-16, T-24, T-40, T-72, T-136, T-264,
+ # T-520, T-1032, T-2056, T-4104, ...]
+ # So mine a number of blocks > 4104 to ensure that the first window of
+ # received headers during a sync are fully between locator entries.
+ BLOCKS_TO_MINE = 4110
+
+ self.generate(self.nodes[0], BLOCKS_TO_MINE, sync_fun=self.no_op)
+ self.generate(self.nodes[1], BLOCKS_TO_MINE+2, sync_fun=self.no_op)
+
+ self.reconnect_all()
+
+ self.sync_blocks(timeout=300) # Ensure tips eventually agree
+
+
+ def run_test(self):
+ self.test_chains_sync_when_long_enough()
+
+ self.test_large_reorgs_can_succeed()
+
+ self.test_peerinfo_includes_headers_presync_height()
+
+
+
+if __name__ == '__main__':
+ RejectLowDifficultyHeadersTest().main()
diff --git a/test/functional/p2p_i2p_sessions.py b/test/functional/p2p_i2p_sessions.py
new file mode 100755
index 0000000000..4e52522b81
--- /dev/null
+++ b/test/functional/p2p_i2p_sessions.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022-2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""
+Test whether persistent or transient I2P sessions are being used, based on `-i2pacceptincoming`.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+
+
+class I2PSessions(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ # The test assumes that an I2P SAM proxy is not listening here.
+ self.extra_args = [
+ ["-i2psam=127.0.0.1:60000", "-i2pacceptincoming=1"],
+ ["-i2psam=127.0.0.1:60000", "-i2pacceptincoming=0"],
+ ]
+
+ def run_test(self):
+ addr = "zsxwyo6qcn3chqzwxnseusqgsnuw3maqnztkiypyfxtya4snkoka.b32.i2p"
+
+ self.log.info("Ensure we create a persistent session when -i2pacceptincoming=1")
+ node0 = self.nodes[0]
+ with node0.assert_debug_log(expected_msgs=[f"Creating persistent SAM session"]):
+ node0.addnode(node=addr, command="onetry")
+
+ self.log.info("Ensure we create a transient session when -i2pacceptincoming=0")
+ node1 = self.nodes[1]
+ with node1.assert_debug_log(expected_msgs=[f"Creating transient SAM session"]):
+ node1.addnode(node=addr, command="onetry")
+
+
+if __name__ == '__main__':
+ I2PSessions().main()
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
index 76d9b045ce..5030e7af26 100755
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -72,6 +72,13 @@ class AcceptBlockTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
+ def check_hash_in_chaintips(self, node, blockhash):
+ tips = node.getchaintips()
+ for x in tips:
+ if x["hash"] == blockhash:
+ return True
+ return False
+
def run_test(self):
test_node = self.nodes[0].add_p2p_connection(P2PInterface())
min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())
@@ -89,10 +96,15 @@ class AcceptBlockTest(BitcoinTestFramework):
blocks_h2[i].solve()
block_time += 1
test_node.send_and_ping(msg_block(blocks_h2[0]))
- min_work_node.send_and_ping(msg_block(blocks_h2[1]))
+
+ with self.nodes[1].assert_debug_log(expected_msgs=[f"AcceptBlockHeader: not adding new block header {blocks_h2[1].hash}, missing anti-dos proof-of-work validation"]):
+ min_work_node.send_and_ping(msg_block(blocks_h2[1]))
assert_equal(self.nodes[0].getblockcount(), 2)
assert_equal(self.nodes[1].getblockcount(), 1)
+
+ # Ensure that the header of the second block was also not accepted by node1
+ assert_equal(self.check_hash_in_chaintips(self.nodes[1], blocks_h2[1].hash), False)
self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
# 3. Send another block that builds on genesis.
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index d07b144905..227e255536 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -452,8 +452,9 @@ class BlockchainTest(BitcoinTestFramework):
# (Previously this was broken based on setting
# `rpc/blockchain.cpp:latestblock` incorrectly.)
#
- b20hash = node.getblockhash(20)
- b20 = node.getblock(b20hash)
+ fork_height = current_height - 100 # choose something vaguely near our tip
+ fork_hash = node.getblockhash(fork_height)
+ fork_block = node.getblock(fork_hash)
def solve_and_send_block(prevhash, height, time):
b = create_block(prevhash, create_coinbase(height), time)
@@ -461,10 +462,10 @@ class BlockchainTest(BitcoinTestFramework):
peer.send_and_ping(msg_block(b))
return b
- b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1)
- b22f = solve_and_send_block(b21f.sha256, 22, b21f.nTime + 1)
+ b1 = solve_and_send_block(int(fork_hash, 16), fork_height+1, fork_block['time'] + 1)
+ b2 = solve_and_send_block(b1.sha256, fork_height+2, b1.nTime + 1)
- node.invalidateblock(b22f.hash)
+ node.invalidateblock(b2.hash)
def assert_waitforheight(height, timeout=2):
assert_equal(
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 5a870de6c7..2c33617750 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -635,7 +635,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.log.info("Test fundrawtxn fee with many inputs")
# Empty node1, send some small coins from node0 to node1.
- self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True)
+ self.nodes[1].sendall(recipients=[self.nodes[0].getnewaddress()])
self.generate(self.nodes[1], 1)
for _ in range(20):
@@ -661,7 +661,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.log.info("Test fundrawtxn sign+send with many inputs")
# Again, empty node1, send some small coins from node0 to node1.
- self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True)
+ self.nodes[1].sendall(recipients=[self.nodes[0].getnewaddress()])
self.generate(self.nodes[1], 1)
for _ in range(20):
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 37d0c4f87e..844ad748bb 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -186,6 +186,7 @@ BASE_SCRIPTS = [
'wallet_signrawtransactionwithwallet.py --legacy-wallet',
'wallet_signrawtransactionwithwallet.py --descriptors',
'rpc_signrawtransactionwithkey.py',
+ 'p2p_headers_sync_with_minchainwork.py',
'rpc_rawtransaction.py --legacy-wallet',
'wallet_groups.py --legacy-wallet',
'wallet_transactiontime_rescan.py --descriptors',
@@ -330,6 +331,7 @@ BASE_SCRIPTS = [
'feature_blocksdir.py',
'wallet_startup.py',
'p2p_i2p_ports.py',
+ 'p2p_i2p_sessions.py',
'feature_config_args.py',
'feature_presegwit_node_upgrade.py',
'feature_settings.py',
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 36fcdb36d6..d7850b41ac 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -24,6 +24,9 @@ class AbandonConflictTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [["-minrelaytxfee=0.00001"], []]
+ # whitelist peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index d49bca6855..d2ed97ca76 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -55,6 +55,9 @@ class WalletTest(BitcoinTestFramework):
['-limitdescendantcount=3', '-walletrejectlongchains=0'],
[],
]
+ # whitelist peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index adeb551895..5d3d78c2dc 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -26,7 +26,7 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.extra_args = [[
- "-dustrelayfee=0", "-walletrejectlongchains=0"
+ "-dustrelayfee=0", "-walletrejectlongchains=0", "-whitelist=noban@127.0.0.1"
]] * self.num_nodes
self.setup_clean_chain = True
self.supports_cli = False
@@ -585,15 +585,9 @@ class WalletTest(BitcoinTestFramework):
# ==Check that wallet prefers to use coins that don't exceed mempool limits =====
- # Get all non-zero utxos together
+ # Get all non-zero utxos together and split into two chains
chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()]
- singletxid = self.nodes[0].sendtoaddress(chain_addrs[0], self.nodes[0].getbalance(), "", "", True)
- self.generate(self.nodes[0], 1, sync_fun=self.no_op)
- node0_balance = self.nodes[0].getbalance()
- # Split into two chains
- rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')})
- signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
- singletxid = self.nodes[0].sendrawtransaction(hexstring=signedtx["hex"], maxfeerate=0)
+ self.nodes[0].sendall(recipients=chain_addrs)
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
# Make a long chain of unconfirmed payments without hitting mempool limit
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 9a481b290b..f4ae697292 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -53,6 +53,7 @@ class BumpFeeTest(BitcoinTestFramework):
"-walletrbf={}".format(i),
"-mintxfee=0.00002",
"-addresstype=bech32",
+ "-whitelist=noban@127.0.0.1",
] for i in range(self.num_nodes)]
def skip_test_if_missing_module(self):
@@ -86,7 +87,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.test_invalid_parameters(rbf_node, peer_node, dest_address)
test_segwit_bumpfee_succeeds(self, rbf_node, dest_address)
test_nonrbf_bumpfee_fails(self, peer_node, dest_address)
- test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address)
+ test_notmine_bumpfee(self, rbf_node, peer_node, dest_address)
test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address)
test_dust_to_fee(self, rbf_node, dest_address)
test_watchonly_psbt(self, peer_node, rbf_node, dest_address)
@@ -232,7 +233,7 @@ def test_nonrbf_bumpfee_fails(self, peer_node, dest_address):
self.clear_mempool()
-def test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address):
+def test_notmine_bumpfee(self, rbf_node, peer_node, dest_address):
self.log.info('Test that it cannot bump fee if non-owned inputs are included')
# here, the rbftx has a peer_node coin and then adds a rbf_node input
# Note that this test depends upon the RPC code checking input ownership prior to change outputs
@@ -250,8 +251,27 @@ def test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address):
signedtx = rbf_node.signrawtransactionwithwallet(rawtx)
signedtx = peer_node.signrawtransactionwithwallet(signedtx["hex"])
rbfid = rbf_node.sendrawtransaction(signedtx["hex"])
+ entry = rbf_node.getmempoolentry(rbfid)
+ old_fee = entry["fees"]["base"]
+ old_feerate = int(old_fee / entry["vsize"] * Decimal(1e8))
assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet",
rbf_node.bumpfee, rbfid)
+
+ def finish_psbtbumpfee(psbt):
+ psbt = rbf_node.walletprocesspsbt(psbt)
+ psbt = peer_node.walletprocesspsbt(psbt["psbt"])
+ final = rbf_node.finalizepsbt(psbt["psbt"])
+ res = rbf_node.testmempoolaccept([final["hex"]])
+ assert res[0]["allowed"]
+ assert_greater_than(res[0]["fees"]["base"], old_fee)
+
+ self.log.info("Test that psbtbumpfee works for non-owned inputs")
+ psbt = rbf_node.psbtbumpfee(txid=rbfid)
+ finish_psbtbumpfee(psbt["psbt"])
+
+ psbt = rbf_node.psbtbumpfee(txid=rbfid, options={"fee_rate": old_feerate + 10})
+ finish_psbtbumpfee(psbt["psbt"])
+
self.clear_mempool()
@@ -604,7 +624,7 @@ def test_no_more_inputs_fails(self, rbf_node, dest_address):
# feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient
self.generatetoaddress(rbf_node, 1, dest_address)
# spend all funds, no change output
- rbfid = rbf_node.sendtoaddress(rbf_node.getnewaddress(), rbf_node.getbalance(), "", "", True)
+ rbfid = rbf_node.sendall(recipients=[rbf_node.getnewaddress()])['txid']
assert_raises_rpc_error(-4, "Unable to create transaction. Insufficient funds", rbf_node.bumpfee, rbfid)
self.clear_mempool()
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index eb305c5fa2..9f0f694a3e 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -26,6 +26,10 @@ class WalletGroupTest(BitcoinTestFramework):
["-maxapsfee=0.00002719"],
["-maxapsfee=0.00002720"],
]
+ # whitelist peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
+
self.rpc_timeout = 480
def skip_test_if_missing_module(self):
@@ -150,7 +154,7 @@ class WalletGroupTest(BitcoinTestFramework):
assert_equal(2, len(tx6["vout"]))
# Empty out node2's wallet
- self.nodes[2].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=self.nodes[2].getbalance(), subtractfeefromamount=True)
+ self.nodes[2].sendall(recipients=[self.nodes[0].getnewaddress()])
self.sync_all()
self.generate(self.nodes[0], 1)
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 686a365584..6c41b468b6 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -20,6 +20,10 @@ class WalletHDTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
self.extra_args = [[], ['-keypool=0']]
+ # whitelist peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
+
self.supports_cli = False
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index 525b91a6e0..9744009af8 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -35,6 +35,9 @@ class ImportDescriptorsTest(BitcoinTestFramework):
self.extra_args = [["-addresstype=legacy"],
["-addresstype=bech32", "-keypool=5"]
]
+ # whitelist peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
self.setup_clean_chain = True
self.wallet_names = []
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 7ae3a40eaf..f1d7de2f27 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -18,6 +18,8 @@ from test_framework.wallet_util import test_address
class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ # whitelist peers to speed up tx relay / mempool sync
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 36dbec467b..204bd67fb1 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -23,6 +23,8 @@ class ListSinceBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
+ # whitelist peers to speed up tx relay / mempool sync
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 6552bfe60c..4ef259efe1 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -29,11 +29,11 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
self.log.info("Create a new transaction and wait until it's broadcast")
txid = node.sendtoaddress(node.getnewaddress(), 1)
- # Wallet rebroadcast is first scheduled 1 sec after startup (see
+ # Wallet rebroadcast is first scheduled 1 min sec after startup (see
# nNextResend in ResendWalletTransactions()). Tell scheduler to call
- # MaybeResendWalletTxn now to initialize nNextResend before the first
+ # MaybeResendWalletTxs now to initialize nNextResend before the first
# setmocktime call below.
- node.mockscheduler(1)
+ node.mockscheduler(60)
# Can take a few seconds due to transaction trickling
peer_first.wait_for_broadcast([txid])
@@ -60,7 +60,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
twelve_hrs = 12 * 60 * 60
two_min = 2 * 60
node.setmocktime(now + twelve_hrs - two_min)
- node.mockscheduler(1) # Tell scheduler to call MaybeResendWalletTxn now
+ node.mockscheduler(60) # Tell scheduler to call MaybeResendWalletTxs now
assert_equal(int(txid, 16) in peer_second.get_invs(), False)
self.log.info("Bump time & check that transaction is rebroadcast")
@@ -68,8 +68,8 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
# but can range from 12-36. So bump 36 hours to be sure.
with node.assert_debug_log(['ResendWalletTransactions: resubmit 1 unconfirmed transactions']):
node.setmocktime(now + 36 * 60 * 60)
- # Tell scheduler to call MaybeResendWalletTxn now.
- node.mockscheduler(1)
+ # Tell scheduler to call MaybeResendWalletTxs now.
+ node.mockscheduler(60)
# Give some time for trickle to occur
node.setmocktime(now + 36 * 60 * 60 + 600)
peer_second.wait_for_broadcast([txid])
diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py
index 21941084a3..9caa1fa3d0 100755
--- a/test/functional/wallet_transactiontime_rescan.py
+++ b/test/functional/wallet_transactiontime_rescan.py
@@ -11,6 +11,7 @@ from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_raises_rpc_error,
set_node_times,
)
@@ -158,5 +159,11 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
assert_equal(tx['time'], cur_time + ten_days + ten_days + ten_days)
+ self.log.info('Test handling of invalid parameters for rescanblockchain')
+ assert_raises_rpc_error(-8, "Invalid start_height", restorewo_wallet.rescanblockchain, -1, 10)
+ assert_raises_rpc_error(-8, "Invalid stop_height", restorewo_wallet.rescanblockchain, 1, -1)
+ assert_raises_rpc_error(-8, "stop_height must be greater than start_height", restorewo_wallet.rescanblockchain, 20, 10)
+
+
if __name__ == '__main__':
TransactionTimeRescanTest().main()