aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/combine_logs.py40
-rw-r--r--test/functional/data/blockheader_testnet3.hex548
-rw-r--r--test/functional/data/wallets/high_minversion/.walletlock0
-rw-r--r--test/functional/data/wallets/high_minversion/GENERATE.md8
-rw-r--r--test/functional/data/wallets/high_minversion/db.log0
-rw-r--r--test/functional/data/wallets/high_minversion/wallet.datbin0 -> 16384 bytes
-rwxr-xr-xtest/functional/example_test.py5
-rwxr-xr-xtest/functional/feature_abortnode.py48
-rwxr-xr-xtest/functional/feature_assumevalid.py6
-rwxr-xr-xtest/functional/feature_bip68_sequence.py21
-rwxr-xr-xtest/functional/feature_block.py115
-rwxr-xr-xtest/functional/feature_blocksdir.py8
-rwxr-xr-xtest/functional/feature_cltv.py21
-rwxr-xr-xtest/functional/feature_config_args.py53
-rwxr-xr-xtest/functional/feature_csv_activation.py97
-rwxr-xr-xtest/functional/feature_dbcrash.py25
-rwxr-xr-xtest/functional/feature_dersig.py16
-rwxr-xr-xtest/functional/feature_fee_estimation.py32
-rwxr-xr-xtest/functional/feature_includeconf.py6
-rwxr-xr-xtest/functional/feature_logging.py2
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py2
-rwxr-xr-xtest/functional/feature_nulldummy.py4
-rwxr-xr-xtest/functional/feature_pruning.py21
-rwxr-xr-xtest/functional/feature_rbf.py16
-rwxr-xr-xtest/functional/feature_segwit.py32
-rwxr-xr-xtest/functional/feature_uacomment.py4
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py2
-rwxr-xr-xtest/functional/interface_rpc.py2
-rwxr-xr-xtest/functional/interface_zmq.py109
-rwxr-xr-xtest/functional/mempool_accept.py1
-rwxr-xr-xtest/functional/mempool_limit.py6
-rwxr-xr-xtest/functional/mempool_package_onemore.py92
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py5
-rwxr-xr-xtest/functional/p2p_blocksonly.py10
-rwxr-xr-xtest/functional/p2p_compactblocks.py31
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py90
-rwxr-xr-xtest/functional/p2p_invalid_messages.py30
-rwxr-xr-xtest/functional/p2p_invalid_tx.py3
-rwxr-xr-xtest/functional/p2p_node_network_limited.py6
-rwxr-xr-xtest/functional/p2p_permissions.py103
-rwxr-xr-xtest/functional/p2p_segwit.py118
-rwxr-xr-xtest/functional/p2p_tx_download.py175
-rwxr-xr-xtest/functional/rpc_bind.py2
-rwxr-xr-xtest/functional/rpc_blockchain.py28
-rwxr-xr-xtest/functional/rpc_createmultisig.py56
-rwxr-xr-xtest/functional/rpc_deriveaddresses.py4
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py158
-rwxr-xr-xtest/functional/rpc_invalidateblock.py6
-rwxr-xr-xtest/functional/rpc_net.py39
-rwxr-xr-xtest/functional/rpc_psbt.py69
-rwxr-xr-xtest/functional/rpc_scantxoutset.py7
-rwxr-xr-xtest/functional/rpc_setban.py47
-rwxr-xr-xtest/functional/rpc_users.py182
-rw-r--r--test/functional/test_framework/address.py9
-rw-r--r--test/functional/test_framework/blocktools.py28
-rwxr-xr-xtest/functional/test_framework/messages.py21
-rwxr-xr-xtest/functional/test_framework/mininode.py37
-rwxr-xr-xtest/functional/test_framework/test_framework.py21
-rwxr-xr-xtest/functional/test_framework/test_node.py31
-rw-r--r--test/functional/test_framework/util.py73
-rwxr-xr-xtest/functional/test_runner.py68
-rwxr-xr-xtest/functional/tool_wallet.py129
-rwxr-xr-xtest/functional/wallet_address_types.py18
-rwxr-xr-xtest/functional/wallet_avoidreuse.py241
-rwxr-xr-xtest/functional/wallet_backup.py1
-rwxr-xr-xtest/functional/wallet_balance.py2
-rwxr-xr-xtest/functional/wallet_basic.py33
-rwxr-xr-xtest/functional/wallet_bumpfee.py17
-rwxr-xr-xtest/functional/wallet_bumpfee_totalfee_deprecation.py54
-rwxr-xr-xtest/functional/wallet_create_tx.py39
-rwxr-xr-xtest/functional/wallet_createwallet.py18
-rwxr-xr-xtest/functional/wallet_encryption.py2
-rwxr-xr-xtest/functional/wallet_import_rescan.py77
-rwxr-xr-xtest/functional/wallet_importmulti.py34
-rwxr-xr-xtest/functional/wallet_multiwallet.py31
-rwxr-xr-xtest/functional/wallet_reorgsrestore.py105
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py3
-rwxr-xr-xtest/functional/wallet_watchonly.py107
78 files changed, 2964 insertions, 746 deletions
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 45ecaabe14..367d0f6916 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -8,6 +8,7 @@ If no argument is provided, the most recent test directory will be used."""
import argparse
from collections import defaultdict, namedtuple
+import glob
import heapq
import itertools
import os
@@ -76,9 +77,17 @@ def read_logs(tmp_dir):
Delegates to generator function get_log_events() to provide individual log events
for each of the input log files."""
+ # Find out what the folder is called that holds the debug.log file
+ chain = glob.glob("{}/node0/*/debug.log".format(tmp_dir))
+ if chain:
+ chain = chain[0] # pick the first one if more than one chain was found (should never happen)
+ chain = re.search(r'node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name
+ else:
+ chain = 'regtest' # fallback to regtest (should only happen when none exists)
+
files = [("test", "%s/test_framework.log" % tmp_dir)]
for i in itertools.count():
- logfile = "{}/node{}/regtest/debug.log".format(tmp_dir, i)
+ logfile = "{}/node{}/{}/debug.log".format(tmp_dir, i, chain)
if not os.path.isfile(logfile):
break
files.append(("node%d" % i, logfile))
@@ -164,25 +173,26 @@ def get_log_events(source, logfile):
def print_logs_plain(log_events, colors):
- """Renders the iterator of log events into text."""
- for event in log_events:
- lines = event.event.splitlines()
- print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
- if len(lines) > 1:
- for line in lines[1:]:
- print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
+ """Renders the iterator of log events into text."""
+ for event in log_events:
+ lines = event.event.splitlines()
+ print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
+ if len(lines) > 1:
+ for line in lines[1:]:
+ print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
def print_logs_html(log_events):
- """Renders the iterator of log events into html."""
- try:
- import jinja2
- except ImportError:
- print("jinja2 not found. Try `pip install jinja2`")
- sys.exit(1)
- print(jinja2.Environment(loader=jinja2.FileSystemLoader('./'))
+ """Renders the iterator of log events into html."""
+ try:
+ import jinja2
+ except ImportError:
+ print("jinja2 not found. Try `pip install jinja2`")
+ sys.exit(1)
+ print(jinja2.Environment(loader=jinja2.FileSystemLoader('./'))
.get_template('combined_log_template.html')
.render(title="Combined Logs from testcase", log_events=[event._asdict() for event in log_events]))
+
if __name__ == '__main__':
main()
diff --git a/test/functional/data/blockheader_testnet3.hex b/test/functional/data/blockheader_testnet3.hex
new file mode 100644
index 0000000000..882133aa2b
--- /dev/null
+++ b/test/functional/data/blockheader_testnet3.hex
@@ -0,0 +1,548 @@
+fork:0000002043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea3309000000000943e54375082c03172552ae841bab31ebf2463484574f6ce6fe9c3723e3defb719a485dffff001db8b63209
+fork:00000020da2809ab72cf2502ecb29137dbe63e51fb82fb5babe6c9530dd86dea000000005dfcdc47012c19a2708b53e820c71b529f616a45529d48bad484948c84685d572d9c485dffff001dd3530a08
+0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b672
+0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d23534
+0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b6
+0100000010befdc16d281e40ecec65b7c9976ddc8fd9bc9752da5827276e898b000000004c976d5776dda2da30d96ee810cd97d23ba852414990d64c4c720f977e651f2daae7494dffff001d02a97640
+01000000dde5b648f594fdd2ec1c4083762dd13b197bb1381e74b1fff90a5d8b00000000b3c6c6c1118c3b6abaa17c5aa74ee279089ad34dc3cec3640522737541cb016818e8494dffff001d02da84c0
+01000000a1213bd4754a6606444b97b5e8c46e9b7832773ff434bd5f87ac45bc00000000d1e7026986a9cd247b5b85a3f30ecbabb6d61840d0abb81f905c411d5fc145e831e8494dffff001d004138f9
+010000007b0a09f26fdde2c432167d8349681c7801d0128f4dfae4dc5e68336600000000c1d71f59ce4419c793eb829380a41dc1ad48c19fcb0083b8f67094d5cae263ad81e8494dffff001d004ddad5
+01000000a62bc0c08afc1d12e6c6a7eb4a464c848190ac0e44123d5fa63a9ee2000000000214335cde9edeb6aa0195f68c08e5e46b07043e24aeff51fd9a3ff992ce6976a0e8494dffff001d02f33927
+01000000f9e2142a93185496f7b21314d8b6fa736d0a30fa3a6d339ab3a1ba9c0000000061974472615d348df6de106dbaaa08cf4dec65e39cefc62af6097b967b9bea52fde8494dffff001d00ca48a2
+010000001e93aa99c8ff9749037d74a2207f299502fa81d56a4ea2ad5330ff50000000002ec2266c3249ce2e079059e0aec01a2d8d8306a468ad3f18f06051f2c3b1645435e9494dffff001d008918cf
+010000002e9afd58b91f15c3ec9eb0f01ed9d503134da1918b6bb416a9920e700000000029fb495afdb58f3a26d1c90fafec93aed840e2fa37ad6173ba1e7fadb7121ee57de9494dffff001d02e7f318
+0100000027e0ca29a9802c0a2390ecfa90a9bd814fecc54446510e155652dead000000007e8d5344557575c8f018cc62a32e8e0bd80638643b4ec34945ec4662fcab138142ea494dffff001d04acbc3c
+01000000001f3ada9b561378e324e80ee68facd5d232f72f773b86328393054700000000eaf3be35e3f0ace8b6abdeb5509d72999eae2329657238b53fa437e319c8e96b99ea494dffff001d027801a8
+01000000781bc7847e15c3b936a6a6a178e38fa29ee6e4916a8a62e10795c69200000000d44c3443fa8bd88bf32b94b9257f09ce6fb6ec0d5420504d631568f8685200dfa1ea494dffff001d01f781d0
+01000000133991a938b505ee8f6f347f313c3372d82a9d8b42b08b0dd0fc086400000000a0ef58c239e0197a65aa248c2cf52c437d8c8ea30d1b835e630a87c941f7d4e9adea494dffff001d030ef2e0
+0100000028d34cdb13e555032e4bec55fcce3d0fef8212803fb1bab851e1259400000000542c71544b9f28bd5a6fec95ecd509ae49d0b04f8718c685d0751f71d38285d0c3ea494dffff001d056b3115
+010000006b00cf1ce31b33fe1e2c4648a0834dedd972ffb2a2f341f75ad7cbc400000000adebf7afcbf176f765aec16b74d92896f55c3d65e14dd1a8becee0871000291751eb494dffff001d006f85e8
+0100000043a78ddf30a2d28a42cc66f90d13cb8211ee0fca9dbf8a4cce8c19fe000000004edbd2b89cb6d6fd69b575a62bd4e3103b1e0ce19e31bccf9a093ad8ccd753cf7deb494dffff001d0591a0b3
+01000000489ac81592595a4004e14331cb096ffef12b1daf709f6378e9c3558d00000000c757bebd6f2c2c071a3cf739a4cf98b27441809790a5cf40652b46df8a98a473b0eb494dffff001d011aedb6
+01000000a9c570a45d959023551f9a694ace9c12206174f21383f30949ca3b9b00000000eaf93dbbfb3551a1ff8b6bd5ba4cea7508e790c23cd07b9d9e791936a79d5fd4b3eb494dffff001d0385a7dd
+01000000d35d5fa860dc70c8bdaf12f18e16d8b4cc29d141c28d59cc317fe5ed00000000507dae091a9657b6c073863ca71ba6989a2cf4417fb81e940668568a35d34a7119ec494dffff001d00effec3
+0100000073379e3ff3dffd006e0090e52ac571a9a309490a23e64d15f8af291a0000000051f1c5b2b7c8f980e7715b4d3ce0180f99c44a16fc9c00ede2f5984b8d7cc22d16ed494dffff001d0082467f
+01000000869845a3343adddfa5b1f534b507d9b67c3685b0f1d89d526cdbd34200000000823623e8c6fe2c449065d2c0ae57aeb4bfd8e9687126a6c99d1ce916e2fca63f4ded494dffff001d034f940c
+010000005da49f64cf0025ab1111651d94748b00bdb00b780744b88b42f962c200000000fee9e254a5a74c858297e89ebcd2305ad2707a8acc131ad07f6abc0d8e38def969ee494dffff001d05c512e2
+010000006a65bc120bf3e6dfadc3b9543e48f8876cb826aed0d8f809bc34bb220000000090f489f48c88442aa7d9250f743b386558ba1fa2e7e240e5d32195d56cf1c34ffff0494dffff001d014394ce
+01000000e3f21ff9cc51ef282bf6bebc90e6f96968a36a704452192724c839bd00000000b6d553c98016b66fcb4856ffedc13a2de720288d4c2e8fed86206981259791a23af1494dffff001d017f7044
+01000000e16daff1b16a81a3058d982e79550c9c9ba84a207a8b84ae092eb4b300000000f2f34dd423f99930aee95815b2885906f9cdeaba04a9bb076c1f359c2031732059f1494dffff001d01b322bb
+0100000059ebd22dc26158414c60868355e78ab4b6891345fd97602f6c106d9c00000000e6c5bea3888e891bc5f9f8fcd166d332071d3b434e933763fb20db50e47dad3f5ef1494dffff001d0067a0e5
+0100000015ddbe82f202b27febceb00547dc19653604ef434f080848f22e3b0900000000e26e8971a53396413f0f39b88a697f593993999c8d07fa2dde608111fb2ccbd3daf1494dffff001d04362f37
+01000000a76595e37692f85d5de0438da8e75b5f611fd7b7071816b9ace8ba2b000000004cfb6d8faca8e8e77a71359d2cf0d12d2e52f266591f5fb807aa737c90869d2a81f2494dffff001d040e87a6
+01000000a774311853f32f32d87081529bb0506d5e4e90f7e455bb640081215f00000000228b387354daa9e5d38201811fe746591ab08a66bb3c4fc796a45535acb8c61baaf2494dffff001d01ebfd80
+01000000d0efaa4f6924659f1e0221f910e99f6fae76b36b759b212852d1343a000000005e40d3b65982e929433ee02037a60f05b62e70e6d51f608974fb1f2926169398bff2494dffff001d05c41c3a
+010000004c600547d8ed4b9dc946bb455f64917131dd98974bf2dfda05afc3a800000000e5b01ac4a611211847b0777b9ba9e396b0b7348ac401041e4fb6168ed091691ccef2494dffff001d037a60b7
+01000000f571553e5aa32b8374bd5a0f3c58834a46e05727b64dbe62a942376600000000a91cac1e92c7c597af565594565f5b54e658fd2e022e4fc35eb92aa165d9aa4fd3f2494dffff001d02687811
+01000000d8273e218df68e333782ab3b79929a8609404bd85bc225b46debba3700000000e91f9128f70a0502d338221316a0a3a1f4181821f9d2220c3fce7b5cf8e305d514f3494dffff001d01f22226
+01000000ad615025da247d14a4f092f21c4fddc6d1a84e0b4b0929f9ccf81c180000000069cabd55fc74596566ed1548b6b4ac23943d33e817028d8bc8695a7bba82256e6df3494dffff001d00e066e2
+01000000e7da808f2e62c3ec1b443e2979c972f5f69f32146cb4385bcb6fdca2000000002f4092fa4879ecc1a39471c41f8fae20f10ad2204bcda34e79ac37cbbda973757df3494dffff001d0316a8a6
+01000000928b86c36f27d22c7d2baf27f31b50bbec7754d33d12aa37342723db00000000ece3fdbc63b66327abb251165ac0e19b1a02fb79295c9b6eb8b38b68c1c59107dff4494dffff001d05bcddf0
+0100000088c96b45d3e252cdc38780843e3679d74e858a1d218f9e3e0866335e00000000432cba2143d62f6349faf4f7956f7354733518c22a309284d96236a9d8c9616e0ef5494dffff001d026ebb97
+01000000885d76fd42926155ff9ebb1d3c41a517d7beb70564b98c3608719f740000000011a3bb257acf328a5bf1258bbb653b88a40b2d2a66db695a1ff1aa9d31d5186590f5494dffff001d02bcabdb
+0100000093f416076c9b2eb0d147e8ce3bac03aa0784e44a6de6fc6f61183a2800000000be46ca90dd31022ee15f74c221208eadf1840e208a6adcaf127a60cc61d959c4d2f6494dffff001d02d68fd2
+01000000f9da861df4a86158e751c062e0a82d3c90e9038330f6447df4aee3d000000000972534907cdd1bc56f21adde0850dd17d988711a5c36ff81ec84610b4f762e9ae3f6494dffff001d00e96635
+01000000303a04a9f551e93a8ca88ceaba1c1d28892323564988ab7f99a9c0d70000000087a11f1aa5769968461b251ca9229f59acad999284e29976331cc13da46432364ff7494dffff001d012c54d2
+010000006b82062aabb19e4cc89addeded5c65ec28684cb54d30d463719f094800000000a98137fab92249e632091c38e3fe4c7fd8bba11bc956fe83c41357e217929e1e8ff7494dffff001d01edcd15
+01000000f7332e78d21adde6e52aa20362c71c1fd7bc745b3edff6e72497067500000000d521571198d3d2c4124b8bd9ba7842716b3bdd93236a92ca6af7c0a7adf9633c2df8494dffff001d031b1c13
+01000000c9ebcb8ea6f7e6a611117d0268639625cee28155c88708b4c09d4aea000000002d4c50f85979f0d2491d015206d867b273b353c253a173abf18dfbc5d96d088e50fa494dffff001d0397f4fc
+01000000562c889f95c49db05f6a33277aae9bfe68e92f00a5d6f67ac8c366a100000000b7b0896c4a53b6aab15282cf53e1119676d89be0eda11064cadc40dc8ec194dc66fa494dffff001d01f0369e
+01000000d41cef71d625aa5380f6cdc6452c67951e7e4f5b27b7904b4c2da413000000001c1e1096ab473aba614651fb98f47a375c03ec470daccff39a2d2d65bfd881b47bfa494dffff001d02454a15
+0100000095359a35957b89dd268576d562f49db7939baeda6de4855426ab5d9c000000006f68039da08fc314bcc71631be6f4b2ef5e0a2f9491fc078b11fad3dd49ac287a0fb494dffff001d02fbc213
+010000007d505f65addb5a3b50eb33cc5cd3bcafe03ee597c4027166aad2d2630000000090d221539ba3dabdcf0eacfa9d63f272c59dcc07e5e193271a24f4c11caf2c24d9fb494dffff001d05542cd6
+0100000013d5bb77b9235002acc75014e0c061c79de1752d3edc6859b4c0df7a000000000d17f332abe46c092877537ae764aa99e9b25d6bdd94f4007eac43f4861cf4675cfc494dffff001d02f62def
+01000000612ec5ae40cb2a58e18d3124fd70664a4fcb9329f7d268d4bcaefacb00000000f7bc31a9984831440cfae529df94ba1680fa15b4490454454920ea8947af4a3765fc494dffff001d01c6c1af
+01000000f13ce1836e92f0d12e4892eb229cbf6d50b9b2080af16b01f00b25de000000003f8f64092fddae84dad92736fb7d350e25bda6118bb1c660b06a4a5730fd352815fd494dffff001d055e65b0
+01000000ee24ae636bd70cadac603bf8cc631369bafbdfa8ac8effbc7dcb0fa5000000006faf8082545aef971aa4f92bd411947929c4949362300cd1302ec8bf74091fbe35fd494dffff001d0139a790
+010000006350a3cd2181f0eb75fa8c4022634ad85b6b9e1f9b7346a2eef5517900000000a09d738f407e2dcc888e8ecbde93458d8599720e0359a380d503ee56c688ef72f9fd494dffff001d02661caa
+01000000a29d92b014057dbff2775280b8bae8b3877fa9a345f4972034c0711b00000000447bae5734b6c67dc36ae088869c18740a836d7a9feff0b349efdd7373a21358fdfd494dffff001d01b13c06
+01000000dcbf4a9455bf33482c2e17640cb89711468c0def1ba9f6e26e5a4aed00000000b555247649a364adb88b07afac3aa2f9ddf5c379154cb17d22f69fd74c84a2e24afe494dffff001d03144aac
+010000000429b2aa02abcb8275705dc6482230dcb7c8678388fe7d75022c1ed800000000d5efd425dce6deb5750306ce8a0e5d045e0a607ee0b530866833e956dbeea0575ffe494dffff001d026ba2bb
+01000000871d8a30b3b39373d71fb9c5c7d8d7a3c005ad688d9c8ca974cd330e000000005ec14d2b9adfccae05f78556b12fe2ca03cddd8bd67896fa5c6b6fb090e90177a3fe494dffff001d0147b26b
+0100000019fd8a4e03a52e43322506693b696af00b828361e2a0968898708e43000000003853a270a7d9e6ad80fbf911146428c7ed63b2a1154df748f5a980fe20e9e8e2b3fe494dffff001d04538437
+01000000d250d67bd2679caba305e5e13698c7880098a1a4383c2f464fe9006d00000000d793e7c2c9416eae3d8d81046985db5f3d1df7922e869fda6ec5b22181ee46f725ff494dffff001d02391748
+010000009d83b8dbaae3dee980529b6dbd307df153a22b5b0fc35ea694a1faad000000003cfd458875824fadce673ca8c3125fd12c36b7230035b5f056a12d4e0d6b7e6b7eff494dffff001d0448653d
+01000000a7d1ff0415c1d1be8a11c7dc20557e001d2fd96c0f396f52969e330d00000000ca20885f915b1d3c5e614cf76067fcf8d9894204db63664d512806ceab58ea27d2ff494dffff001d0327cc00
+01000000d98a10f08f4a7aa698ba8564a85bf821b747c7a44f656fb2e3b9335f00000000dad4bbd7663a69c07ff9579434972bb203d0d78d07858ddc7ca32513101c964e3d004a4dffff001d04df7568
+0100000031965a378dbb0d024df444b3ebf894f370d28df8abdd54565b598b9e000000007ad93f6eed4656fa7c0a606c08b067be35dfa5392eda5d77f133dabdfd08739794004a4dffff001d04facfab
+0100000004cc9e4dad8b5d2166fb175e7bfe13ad56018dc81255d335336b8b38000000002d011ec2248c2899ca72a01673535807e2cd0fbcd4465ebafe2a002f3f6c9ec1a3004a4dffff001d04d4b0a1
+010000002093e7853950d588f1a36601643a97b7e187f6b7b274ae43d82410a6000000008ea10bf2d0d535c64d9db748aec8723566b270e0becacdb07438273654e59feea8004a4dffff001d04d4db49
+01000000eff17d4b5360e16151019597f732fc8f6e3b19ecf0c88e2ab1a2bd3a000000005d9b9c1d04856052c8f01497c9ce05d01c4c80c1c4296b15549ace3e04e2cc24e2004a4dffff001d02358897
+01000000bf5ea56c32a49508f0987cf0b18513d285edbe987b0dad25ceef3f1b00000000ba933ef648e2544227c7db41de45f0cf39aa612478c126e5ce5393668e230f1213014a4dffff001d044c6575
+0100000091ff63e6ab577bdc347fc87265fc316d53303bf28d0294a24791143300000000a97e4a6f79bb595fe0239f31549d5b06e13e7d5062e3707ebab0f38f0df5d1832e014a4dffff001d0083c449
+01000000bc749fb377c9a937702bd4ff35d376b43f5bc56726029a93a38e8232000000002bad8475b38ccd82eb98377132453c72706ae9626de3f913e8da2cca6795481166014a4dffff001d0594c61b
+01000000a02349924ea393906e7fe3ffc2b9d152fdf55bcc1bace5251517168100000000448e52c8902abd28f82f3ff5eec8c97ab16b71b61674713cbc4f9a896f733b5ba5014a4dffff001d05c09af9
+0100000011f5cea7297afda99c9a141c4d438ff7708f451b571b4aa57b4b69f00000000087550789e53e3c66c4ce24d145f615c95f593d7557fa3ae2eee2a1f985549f9bfc014a4dffff001d00e035ee
+010000004ba4c615f54d1557c5f691cb614cecb9edbad0011ebbd3b94fd458ee00000000c9782c5584bbe7c3a51f048dbf1e428e0b0e092607ea6514c401f13a45afd2b6cd024a4dffff001d049ecd2a
+01000000718886f10113146ac974a449c8b5bb205e0307f42d1b81990cf1991e00000000b88f0b4d80b631a24a15d7c640ecc15ee82188bb16d692be42467e21b3971c1421044a4dffff001d03e07f40
+0100000081fbffbf5c27e4e908c14bc8f303a4d91bb71ae05445e839c531ec81000000002aefeed1a7378f70e1f434bde89af706e1777993d25d9a95f5b4f4a0d83bcda8d8044a4dffff001d02cda9e0
+010000006d7f371b437cc054548511f0f9ec50b6e04bb4f3a4915a099c3bad36000000005b72bacbca2f6bb848d4753733f6427849b1aa4805b244e2751c38ff8d3e339c41054a4dffff001d0018ffae
+01000000b82c2d751368b79004a8e15e5b9f1bc7f620d5de2d6105d83d8ec2f6000000000cdfbd9bd57b78356abb0d07551b0c5c20d5258091604be05674d4d19455df2fb8054a4dffff001d003c4d86
+010000006efef1bc9d35256bbe500ee9c92230e0f987296e373e20ee7f3da4710000000030a2d3961a799e229a81337790254ebaefa055bb85ec7f03e5c8e9c3250eb35ad7054a4dffff001d0514a76d
+01000000e789ed685960b594652b9b83a28ded0995725d6ad82b2f358058cbe80000000035b73fb2084e2e8faa28334b17c12668004b7cc8965831aaba65b38b7c54ba97ff064a4dffff001d055247e5
+010000000606ede205a1002e0995f8d009de821d488e22fb37167bcc9120afbe00000000f6f0d5c7f8204a7142f2e37a7cb406e0a57fd7c9784c12111c55a1cde5418ed11a074a4dffff001d01a5f8d1
+010000006da3e0f7fa132425557519508e4e97cbec301a33e25d4b65ac054a0d00000000d657340c13f9f8accfc3af62529a25d3d9e656caf2993fd3166f73850f397c228e074a4dffff001d0309686d
+01000000cb9c32345bb264b3b2a6e96c8843f65c9598e4948737c569c0a00073000000000894a15ed694c745d87d2135f90eb9dcf8b454937b48c82132d52405e0d4c4689f074a4dffff001d02d5845a
+0100000095349f59ed0c0ce5ab0de43b9d55fbc7b8afac7eb6dacebeaaf290f100000000df9bf22064b03cf08e658257b5d19662fdbe3d7acd27894cef0d3f9ff5a657bbbc074a4dffff001d00a13d79
+010000000be1a15c8ae486da44f0585514eea60780091e52e35f838cd1572bf50000000077333896bed3021acee751bf0c073d95bbdc99125f4298c052db8998bf7c3270f9084a4dffff001d008f86bf
+01000000c0b7b630de7b1bd92c4be9d32e19faeadd68f60316ec97db96eaec0f000000005e3c98a964d941f4abd129531d0ee81cd5e7dc098179c3188ba36ccd1e5f9fff7e094a4dffff001d03d8a118
+01000000ae3c51439dc8f2bdd807e1d88c25a5a0b1a3005bcbb50bbb4e48493f00000000ac719d460d514cc4489597ac78e995276865ac07d7606a313c12b16e769b9294f50a4a4dffff001d04735f73
+01000000ce12350c698e84085e7b62c039249c63cb6a6cc9404776c9cc7fba8800000000ca128b2e74fafb345ad249d61c538f75a2a230bbf2266d470d47986555894deb370b4a4dffff001d001c0a17
+010000003dcca8252a636d4c0b8bc4ed9285b749434aaebc29965fc691a635ea00000000baebe5ae323c2bc5c316f6a8f1947b4666c7707c9ae127a03e6429fea7624ae53b0b4a4dffff001d00b4a44c
+01000000f6cd7f4cec06d5c6aea54e64b45f049640680e4cb8249cf18587b314000000004a1b2b51da86ee82eadce5d3b852aa8f9b3e63106d877e129c5cf450b47f5c02480c4a4dffff001d02552721
+01000000c64e139bfae4adcd96860bbef3969b84851dc4d4fd8d06f16cd03698000000005e4b195fd24b314f69f7bb5b0139861b0f07a1286b8f6f42dbb6c82524bfdb93890c4a4dffff001d033b3297
+01000000a3e811a098612235feb5e1acb407cd98132a9d5f4dd99aa8d8b576ea0000000025a13ad0cce0eeae69ddb545bda3b490230e68dbf0687af3ddfa132caebadd7cb30c4a4dffff001d04a39aa9
+01000000ab5ab86fe14c9c765d8cdfd67f9bd5d41505f9f9f67e4da1851f099700000000beb6740250c060fa7b472f4daea18186d47e266dfafc88f088d7efe3ca5a2ac1060d4a4dffff001d0511eae4
+010000006ee9f67721369cabaf6e3e9045b2efb70bc1344256dda634a92eec5200000000d5b0833f82460e5ea635a31c60314eec20bb317f2cdaf354023ed4225e31f264380e4a4dffff001d03a74f10
+01000000b3ef61423747695daed4acef8980b5ef4c8feaaa908b3fdc6fbecdab000000004fd416b35e12b775e2899ba509a06822ed8b6627311364f5195d32ca6a314dbb3d0e4a4dffff001d0255ab96
+010000001c75c30c7bb6391ec7d94f6d52bd2aeb0b8f5224907b0106791ccdd70000000016f86bf97a3dda131f108ab4ab456d5ed3cc5e67eec631d8fa044b95c19f3449410e4a4dffff001d048e1c98
+01000000a8bb0604235d5310973b23a5c797442a1ae8c07b96d8cd33c191b45300000000322948a4806acfeca2b32248d0e183c8eb09d5e5ef48adf33777307635414cc05b0e4a4dffff001d00edcc80
+010000004d37f2af0c42371bb77b52cfe7f539e550e126631dbae6056e54dec80000000014238140ff083fe67f0d5c0627857c4330c4c96187964f42b680ec460ad0ccd6620e4a4dffff001d048185d3
+0100000087e774423a88647a4567d3a15e7099b7f271d8d846066af9776ef4ca000000000d90cd436a707875c28222178146cb93f6b048dc4e7555cf37b96757e3b90a5b740e4a4dffff001d030ccab4
+010000002f3be6a1d59b7786d8601330a47f030fcdf2354275fbafa8f4c129490000000098a23359c17ca2678e2039c8ff9081b18c4913749c9a081ac3f62958f09fa472e10e4a4dffff001d00c7fe6b
+0100000050ac3cacdb94018a26258b82299da1307ccf3e2bf62a8f4acc19e02c00000000e09f513a024d3e13473d7a65f79073b36a90cc228613672d2a47812368ff42d1df0f4a4dffff001d007f726d
+010000000e044393202d6b239c902d3f634e3dfdfa31ae439d339238ab1688e30000000055b5d3d496e196d624a471b818ba0b1778417ae335a544033536654fdda3eef6e80f4a4dffff001d03ed4240
+010000006396e6ba5ef4924d42f4f3114ec7507b81bce51e9e2eb51ca9c53c420000000049af9208af7b7a06d65ce1cfcab6ad9123a8dd7538fb0aca332c63429ff48d59ee0f4a4dffff001d056528d3
+01000000497b15826573b28c3e83a5d0c5ed30cf48b97dd6bd797849144ea2400000000018b50db063333a3261b9b41e887b4aa5b69becdc9967550507c120e22a76496710104a4dffff001d0408eda5
+01000000f5ddc74872eb899f5113e002f642e7b507f871d99a9900edba304aa0000000008f6546b850a14744afc3fe55f76f3959f40799bf4dcefe01ae4dba5903cf2fb553104a4dffff001d018f658e
+01000000918a758ed54c9f495edb24ef3fe0f4432ede25853c324fc0f33a458f000000007a8e49b22114f17b5933fc7a8005421ff8370b8c48fa04c24323e91bd02d701492104a4dffff001d053e6e0f
+0100000035893f7cdeb0e9af7d9fbda1584ef6d5219dfbb141b07b31257a1658000000008b9b3abfbe24d0e375deebb5f41e74949203c00772a678ba69c1126156c5489bfa104a4dffff001d012a69fb
+01000000b6f8c48e94ca346b12373281acbaa08fa54d1cfdcd9c01e020cdda9f00000000be5b4753c6062e3eaed75f5412e43d6dce8d242c5816b436689f795f90536f28cf114a4dffff001d01b6887f
+010000006006db00d70ce04a9940c203dc865b3c5d070f8c2d1295498ccd6c32000000008bc41e410a44b764dacb38c1138a3ff2c038a188a063509c6fed4aacaae72ee67b124a4dffff001d009531e5
+010000005e0dc170558d7b2872ddc85f481531dd823dffa66cc620c065adef7700000000e82e91ade6f25c8c6f4c053aa62d94926324ced07ba2f3aae072b13a2c5dc5f89e124a4dffff001d05ae2281
+0100000056320cab20bc1daf4fe3cf0115f2436523e44c40ebcf8c18e6b5822f00000000ce415eab9cba354ae042c22ac9f06c1a69d7a5dba67136fabef93d82f374dd3e01134a4dffff001d05a0d736
+01000000ccb063ab7d74a4030fd155615f046f95c8068078557568ce6b8092fc00000000ff33b27214141ef3d183b1d2499666c8635a57943ef5f515f4e60515f9ea0064c1134a4dffff001d021fa95a
+010000007f6d7a61bd46dd27be404b8c883b812c2899095462591dcd75a96f1e0000000091777c00b7168a888d7a7db4b5f78758129e79ef909f92a84110b9f33f9c4c5505144a4dffff001d03526c4d
+010000006be8cfe3e176d34d1a46f68b7d20a01ad3f9e2aa6f7540ef6a32573c00000000a870da2f87071c1366a22e77c829a6b85d745ab2279e0333872518d58b8dc0181a144a4dffff001d042c5db7
+010000009cb87223258aba43742de401d0157ee2b4057da95b23e1665880725700000000d54d368cd4243da3793fe3ba2af1570dd44a905d77ecd1a5cbd07cb8f72ad80f30144a4dffff001d048f280c
+0100000052b771a3a85c26bc796ae0841ae894c6ad4527b062c94812d98b9c5800000000eeed0f4d975db8f66788f809ecf8c351d19ff5805276ef31983bc5682548342d52144a4dffff001d01aa3be1
+010000007ca07eb5637ef7696d7bf985b9114de19317a9abdfbe4ee79d8bcb1a00000000d7172956946d5547bd98c6a7040d353c3cb6285fbad096a1780f3b7503539adf3b154a4dffff001d0333fec4
+01000000326f947390bf03abdda16f673d26326d4b159e0b7f732a67286ba8e40000000085701296d47b03f388fd85431c2a9fc817afc9b24870a9a7da850d3a43a8154b71154a4dffff001d0164b7d2
+01000000a45af68df42f6f23ecc47e1cd0f41d47c1e5a26e8343951f9881e51400000000b7c1565d19d406d8c56448571d7e7da2ac559bf9b43887e16e4f416276ccb99b8e164a4dffff001d02ec606d
+010000007eaec4cfdb152a8ecf8ec155b7343e2cc04750be00b96c61a477e57c00000000096a9263a5008a48e1c2b527b922a81cfc269fd401ce429976c587a951ff00798b174a4dffff001d015bd123
+01000000db36ec19328691975cb8c6666866be64b5be79ae42bee9fce3b5db5800000000b5e73d7d102476db3ca2379bdd891b311140b49626ef42937356ccdb8fed589103184a4dffff001d032d07eb
+01000000d8f8a6686ecdeac529caa3ab9ecfa84a5fb62b06849ee09b8331d89e0000000038b408676c2a78fc63ddc1807804d17e8ad9433387cc3cc0edd68c07e4a714b610184a4dffff001d0437e51c
+01000000c54675276e0401706aa93db6494dd7d1058b19424f23c8d7c01076da000000001c4375c8056b0ded0fa3d7fc1b5511eaf53216aed72ea95e1b5d19eccbe855f91a184a4dffff001d0336a226
+01000000bca72b7ccb44f1f0dd803f2c321143c9dda7f5a2a6ed87c76aac918a000000004266985f02f11bdffa559a233f5600c95c04bd70340e75673cadaf3ef6ac72b448194a4dffff001d035c84d8
+01000000769d6d6e4672a620669baa56dd39d066523e461762ad3610fb2055b400000000c50652340352ad79b799b870e3fa2c80804d0fc54063b413e0e2d6dc66ca3f9a55194a4dffff001d022510a4
+01000000e846583e9bd64108b3b89ad3883bec7731ddf1688a4cc8f79530fed800000000d2954cb816c87a9572bf822138dc84b5f6847fb502cce3d6073f9ffe40588571a1194a4dffff001d045d675b
+010000001d72012c553d72f1f75863310ac0450e53a9e9026b9bf9556ca024ee000000001b7142acd57304290a2ade0e2c96d4fbd3ec924a02a5a0cd30c04f0e96265423ef194a4dffff001d041600b6
+01000000d2c5dfbfa04c7b67457c58f55a8d190dc5f8ec5ab94af969dfb748ba0000000069492041bb66f32c9bd69b74e7ba9bff6d4122e931eacf9c89b45eca2c35eb25211a4a4dffff001d03bca431
+01000000880be932720bbf22f1b14da0e6d16c2773f83699935d390e8621533f000000000f5d2500bce42137fe905225ed9a7380eceb7445c89011bfcc740cf2e9985a034e1a4a4dffff001d03d30924
+0100000000b0b174d61c08a92313345717ca7776a75cb67b77662c04ea7d3e2b00000000c8ac0a2fb1c01e0e0a5339d296eb072b2b9f9cb1d410a1fdd69a2c797094dda56c1a4a4dffff001d05225e37
+0100000089535760639df16a512f9caed73be0edf8c9b5466fcea14336f4a1bc000000001527b6224d45722c8ee351976c69c8fca59c11d3daef7abf1d189aab0e959f7ba71b4a4dffff001d0551b67c
+01000000a61d5d887f8fd4c86f7111c2c5a4d0d593665b527cdf84dac7a0d57d0000000069b7df87a13603be78ccb048370aa1d2da0969f3b1822791d24aa921f8e268ffc51b4a4dffff001d055313f2
+010000007f678f2bbdae181d396123431faacd0b956633c30a55a9595ae6657f0000000085daad94e57797b9340c299e483531dfcd0f3c6996da98ffb2ab31bbe34e346a001c4a4dffff001d03ef37a0
+0100000054112b758ce49f1fd22d613250599ffe92c48202b6a477b9289f3d7900000000006302548e973a0d5764711fe84e1900dccedad0de9f054fbaaed3735b70ed62391c4a4dffff001d057c8c98
+01000000bb142886ff32916975d060c649c9119aca0b47e3f169acd3b7f1b9dd00000000ff166532d3f30299c5a82856e3411957dbe35fe7e17c4f58b92b2ef12c399dd7d41c4a4dffff001d04a2a121
+0100000029a936f51d08ad1f1353890300131fb7c04e20606eb48197dc863eb200000000c7bffe64778d6b4815226c6aab915985d8937fb0d3aaaa983bb513c69305caade61c4a4dffff001d0514cd03
+010000007d16758418920cd6d81283aa30108f20f37dc7114076e23025bb881800000000bda8c051f6e99590cbea0919b7a4189e4d3620ff3f46caa4b797fd52e204ff1fcc1d4a4dffff001d047ea807
+0100000087a6243ae1dc858cf91caf8a1f92dc473bdb14203c573b9d9bc134cb000000004caa084ebef276e6c454dff401271b39e55da21a8cd5a3afdf2d0e0f94b94a2dda1d4a4dffff001d039d9a6d
+010000002d0a1a0b18f1f74ee797beef1bd4766a050a3480b5d7457303b5c54e00000000955ec547d5ff2bcbb3c9f108828e431a674576e1de0b8da794bfa3a70b794281ef1d4a4dffff001d0019c619
+0100000060500ea2003736b74596d4a507f5cf001daa55e7c93b53c77d32a32e00000000c545bbc6ae68433be1fbbf0ebf59f22751af853d0a6fd6c944458aa73ec7a014f71d4a4dffff001d03d8b5ea
+01000000cc3d1b428029cae46634a9b96857475b2bc59619b8408e615f65b7b9000000003563548d04e24c89e7706a3f4ad681bebb3017133e87f7434d824db4e51f479b891e4a4dffff001d038a2031
+0100000073da9ccb3fdbdb4a9e3723a4bd5270c70ef3e78f448fad7e77a8eda0000000001f0a3749af61eeb59aff1499892c6641d1a464a26c156608d02cb74c264786ccfe1e4a4dffff001d0284512b
+01000000ef5a98df2a193c1f8a5c271dc4d45de465b7122dfeefe96fedad105100000000cf330295467623ec1378dc6fa312103ad8a210b3e1351f2f4b6a57ac43fcd472071f4a4dffff001d050ac986
+0100000048256cc5b9ec6e7a12c378c93c1dd2ada859c9a9997adca75166c935000000008e3430573cfde2f3e1eece8aefe661dd841bcb665d35832415bab4f7526785229a1f4a4dffff001d02ae4b52
+01000000eb5d7c4b706d8891ddae3ba5bb57fcf509689fbc196f3ef73837f27b00000000226e90ccf96f41e04f011e69a86e18e96c09df6fbba19416132247f1d2a6e4073b204a4dffff001d0488c17c
+01000000e5214de98aa5cdf1766c5129649895816f49ec82e93c4bbda787897f000000000abd68f73823585582e65529a8739e90bd943fc4e214ac00a20ca0369b70d45d7d204a4dffff001d041786ab
+01000000fd4fc04cdc29aaa117b16b2420aadb9bb92fd19ef2a7aec3c40f71250000000078e5266df52051011f39eb29939c8782564563c20b3856f7aaafa6dc52921359e8204a4dffff001d0205fe6e
+010000007252c67173d343874ecfab4d5f57ab5936f2d87f173047c99c20e73500000000d2b61f338da6ac531884c623db2804c0d7eeb84263b501524cbab6d5edfdf56f00214a4dffff001d0027a318
+010000005a7746eb6d1d19cdb24466e0a87a23b6ba8c2e461ba8928edb84253b00000000f1b03cf0680b9ef33fd311f6bbc6db3f1c164f9341f48a02df1905cec4ce241b2e214a4dffff001d03246ebd
+01000000e93f1fd6ddca6d8fdc3ef50fe0f31769200f8fde592a0d5d6f8e1d290000000078966e9f0a2d4452ab2418249fa6fb1a325a04f039d015899141a82aa5a6c05c92224a4dffff001d01d8361e
+01000000dd5f3ae3d2c2876ffbfe0956b914fe72750b160b667b5bef5aaee61400000000434d2b0f298874c3f6d8467c07dea6883a650de00d48298cd6fb48e8322e1058b4234a4dffff001d0161f3ff
+01000000f814899e7f50c4494806f75523c9d8ea6c0198d13f1f14431fb541ff000000005b1015187325285e42c022e0c8388c0bd00a7efb0b28cd0828a5e9575bc040011d244a4dffff001d0579aa5d
+010000000f72c6372c87d91f2df95b0c9e91cafab596f29270cb01cc67186040000000001fe6898ac074a680fe7458ff87a03956db73a880d2ace6539efcc43002bd97ed87244a4dffff001d0434b3b7
+010000007cb39114d2372ca1d5dbcc3a1137cf9314a666349dd79b268be1f15100000000b17cb4572964d7c6d671e7cc67b04b9fb1b68b31e52e6b4f956f3a0b72ccc4ccc5244a4dffff001d057875e3
+01000000f8d8634fa1aab0666f63fbebfd61e0ffc1dcc647e218414c528e17dd0000000073a2c54d536c19f0d09156efbad18ca6f96b1e9f3bc8490342958f24ed8fc32d28254a4dffff001d0130c923
+01000000943aae5118b0abf4ff55e050b234c21d223871e815e9fae6fdaf693c000000006fd85c0213cfe9863573596a4d5f1509ac41a91b572e6c1bdafe46d9249a5fa4e9254a4dffff001d05878bcb
+010000006c6040618ce7a449cc26ad0578a7c897b4464ee32260014fc5ce6bf20000000096567fa4ac682f9bec7e646452d3bd69088000b19bf7a90eaccc197b632fa79bf3254a4dffff001d02a22cd0
+01000000c067deb4fa218c0f26a247766a969af8a475e5c88c004c300c1c69b100000000ec5a827a707edd70451f070665bf6a9e6f4dd8f815b0265296790f24024142b181264a4dffff001d00e76cbc
+010000005cba08b87cefedceed1e60297564a3eb9e9e2bf942bf63f74fdd7f3300000000e3082dfad468d5c0c8e2f8857a999f898081c8cf59e48857997152445a57218095264a4dffff001d05a30a7d
+010000001dcc225203fab8d972215ad2311203570fe49707799e6871908e37d900000000cea1cec6febbeb980af51f052dc20065b95b2d65520205616a95284480a4219abf264a4dffff001d01b05d9b
+01000000644fa81c1f8c64f08712b41b616a24d3e8af833a4f370c188068133a0000000069f2096bbede7015fee2fb307f7d7dd084641b7f4af5c3074dc7b2b6df03277c80274a4dffff001d01dcf233
+01000000ba750a8cc870173cd7f17dff4c23c228282d9aebc2bbd5ebd9449c7b00000000b28b51c3a1a322c8e29c2b6808ded7981dc085cd7fb529184eff6ce556e09ccefc274a4dffff001d02c039a8
+01000000e0f40d912882e77044fee84e325fdfffbb3aa0fb1fe6ba864d5be65d000000006141e05ebe4e62fc76d0c9f1e61a4d17e6509209309f6fdbaab476fa227f1f4fc9284a4dffff001d0306d223
+0100000003e8e5f89f6d5ee55c6e8ee0d4c1e88d8e4a3a5f05f0c9ec32d580e5000000004e2b02e05fea22c5067327060d3c00482569021252423d372cad30746408d0fcea284a4dffff001d0410f81a
+01000000f389178af7b0ea88a83ae251392f7eb336771c8d7ae666d1219f2455000000005f98a8017e8458e6081be384b89f4ed68a6aaee5ac41cc0ad2331929e657deb27b294a4dffff001d04c90ab8
+01000000d8cec9c7b6dd3093ad29b64c234258bce36693f87a4167d6234342cb00000000d5cfb9095ceb210b374686dfc11fb8d8c7932c30b4a3916c7fa4fa7b760a0831122a4a4dffff001d01708f58
+01000000d850850454d9b392ab01e886bacb717a5615f8f70b6a4ef9bf788df50000000075f11e157a482aa640ea8dc7e038ffcbff0e9aa758ff092222fabc325b9c1a56152a4a4dffff001d021f806d
+010000003cf1b7fbfa5978d5ce1cae5a5f454d41f840cebb72ad3d600f551901000000004503bb32aa7568d6abb1df7a05b80be6ffad47e5a55886488736b5c344a41d431f2a4a4dffff001d048df744
+010000002d7f6a8f2dec6f914a7a63052facc546567fbb02742dddb150427ab10000000031ea5d7ad7f128ec4daa2855b7944503da503818a75f049dd6980e636aaf59af242a4a4dffff001d017a6543
+010000008976cdb2c5a16c0929b45f29784ad52cf3db3035f112e562ed42447700000000bee2958fb6624ca2382e4a6a1bd7aedc2c58edcb7266a106c76d4504fd39eb89682a4a4dffff001d04d4a620
+01000000d6d7f91896014f1f4419628791a5bc39263704165d3e14e23895f57b000000001ae0ce43e200fa010ca331ee891ce9ee93d468c602703a23ca2eeb693c1e05e1862a4a4dffff001d025a8bf1
+010000002be250378c6001da52d435aaa1240522ccb14a94880ff0e0d1eee82a0000000046297804a9aabf1d08096b034a84364055573c45d09be862c33ae30beb3b5ab5ae2a4a4dffff001d01a34339
+01000000064ac21081e5a5c6509f634ffae17551bd322e46d3396ac49b8d68b300000000d15854d1e5ba349daf72089f470b24557a2be25105b7831a3f18a62fb8bab677342c4a4dffff001d050c273a
+01000000ad1cbb656f4799fd8e9de9acb70a47d589ef5311dadade94c494ff60000000007546bbac9ae1c8980da6e8c154b368eb4df305b6f3f27ff38f195a13c9ee0484922c4a4dffff001d059b5c46
+010000009f0d835251ab6812b04939a5889a36d50f4d7486ef98a7f61e45f062000000008dab4ee487e33f872140c04372bbdb6c573b7e9e4ec31cb5d8dff36da17bd8f20d2d4a4dffff001d016572b5
+01000000d1105edb3d0105e862f3bc95a034c0d0815a79505b4b68fdcce9083e0000000061573706774bc7a579a7968281e10612b4551195e16c8051381cdd3a6f93f479292d4a4dffff001d039dfead
+01000000d1e7c872bf92ed9d9007926fbe72d976079ae35efd6f81ba4101a98600000000b4bbecee818dd986e5ab82f36dbd5ccc29ab134614e304c0a397e14082fe7bb73e2d4a4dffff001d00b8c45f
+010000009287b073f80a1a91caa1f664b8c9c578837f878e6ff04108db28d9c8000000007dc81aba2560e72756db13b1e427c9fb4bdec85486c65da8aca5bf968a5bf51e412d4a4dffff001d03a98277
+01000000a3f00f008ca15686450bda91266f8d01a99f345c7846e9e5bfcc2628000000005ac244c2a763cbc311a245df0d6f98a29e187165048a9da449d29edddf6b1923d42d4a4dffff001d02df0172
+0100000012ced3b143532ef3999faae421b6bd79c8ad62a4dc8db862cb05bbf70000000060e6e42fad3fdc3d353a22f1699b5ead453eb11343a0e603ffd8c77cdc773be2892e4a4dffff001d04945d60
+01000000f0c39624b690456af00d742f323762471854e1db5c96099adad5e53d00000000a2367f2d2214ef900583269eb812ed05d4a8aba11d40081eef999ef16af1d5ebd72e4a4dffff001d02d98796
+01000000294a2e12b2716816a62d761c624248fcc10ae22cc1a80290432c85cb00000000fc4f1ed498c5f31fe90b10389f12566a3350a5080db1dba1f01f8834e5813ca9e42e4a4dffff001d054ad0b6
+01000000774ed03a366b9909cb163ca0178cc6b42847461c4a672a537fb7aee600000000225666176205fe41ad268ba6f5d15633a5381c18fbb6cd2f83700ef928c58fd7362f4a4dffff001d04928537
+0100000083900a5d5ac952d05976df43ee7e280faf6105038a4afa75c4d8a2a50000000091cd49f33c9f5b51fc61790d25539d2e896e87c8c195cf305b499bdf42e029a0d82f4a4dffff001d03994aeb
+0100000010336b8cdbaaa1bd6ba9b1e41e85e17bec5b68bc8ff043e5731ed09200000000fc5eecfa90d46aeeda36bb1a2f2da61e4f9be81253033ae55625d00acb13ef35f92f4a4dffff001d049e0a22
+01000000ec5b52a76675e3b1de2e75ad45d6498684a0e81092c5ee36e7fbf60b00000000474d71b72f905a8084842ce4202c2ab9795f3abcf51aa7c458349d21eb6e310498304a4dffff001d015d80bc
+010000007af7e899b49494f77c351fe434981ad8b6352ad62527ed9d6d23a8a700000000bcc43ee02af281701574077d6916c07d6bd15cb96c623f6f304de260f616bd57ec304a4dffff001d03a0956e
+01000000f22be93799f6fd1527bfa224601261300d38571dddedd804c0c7b94e00000000ca1cb89732eb51c0ace08a564445a2ee762a2cc819209886cd0a09993951544ffb314a4dffff001d03b10c74
+0100000099146a7924d99cd7a2c1e17ce7206712a8349399b34ec98eb706d3b3000000002c4fb29a89bfe568586dd52c4db39c3daed014bce2d94f66d79dadb82bd8300024324a4dffff001d04edf452
+01000000e5139dd4cf7511d9eddccb69d2c7aa0917cbe49eb660fe9293667aa80000000068e91b23282bef6f06f22e479587d03f6e1a2e4891b5d644541f1e10275e52e142324a4dffff001d03ef54c9
+0100000052a5c0e3a6cc34383a58d939053af3261acf266ba6cfd3165435bb2f00000000a32d9b10b7b75323db98d486828a5ea4f9e7a2609b76e496f6d86e0ab13a31587c324a4dffff001d0031025a
+010000002ed6742b063239cad1841ce6bcf676a59c66e65eb4a0ccf68ac4eceb00000000efbe4df65aa1137ba3efae5236178fe6646d6eb96a177d5394bba48066a87448a7324a4dffff001d053cfc69
+010000004d4a3f638bb32808c3667937b3a0847eb780a3b3fe70875027adac3e000000000ecc7b73d8cd5d377d74d836bb6e3473478554a923154286ddaf6985948fd9d3f9324a4dffff001d05bead3d
+010000007b86d803ae4a477ebf754711cab10b1b7799dcd50fdd6f25cec45e1c0000000033c06971fe80386570f8daafda6e4ab7e72a18624e211481e7b96633c625a52b3a334a4dffff001d01146b3f
+0100000034231ba96cab51515ffaa9831930a46d1a8df9bb83d343690edd9ca000000000848736034ba62c9f4f3410b11d2a5ec921592f40b2ad6b1d442ecb3188049e27f5334a4dffff001d00a7bc36
+010000009427d53219ae3be5d968cd3dbe9f52232b0b32f662bbf74b4c2cab8f00000000755e1e0417036010faf0520d2524c806dda1e3fce2ea99dd4e70a42efe44f64011344a4dffff001d02c03350
+01000000db9740f3109d4450584d3223ec605be1f62457d8af2c22671b93b260000000009e93a056a6515e7916fc049578708d188c2146d3c12638acac92e0b72e076edd72344a4dffff001d037e783f
+01000000de444daddd51d48d32e2119e59629110ecb69dda27ba85d7e85d40e00000000069b02fd420c2e86a575941a89e46d174a4e0fbc1379dbd6f6d88f3ee58613e5087344a4dffff001d001d016a
+01000000a173420cdd0ccf0695a0a341d4f8cd0dc7cd8fd3aa3d01b68066561400000000cf0fb8b5f6fe33f698450ec147ef896ad953e677b4033e50400ec15454d3067fde344a4dffff001d03475525
+01000000343f2b87a053dfebef86bf7108c93e449c6e289e71a75489fe6125130000000088d412c44abb44236383afd182862dde877813143d21d92a3cc373d4bd4017455c354a4dffff001d02b22dbe
+010000004375b71a891e69ba37f9c0cc54373be6796f43ba90b16b12564414a400000000e3abf5981a1bd6457ec0cdcab76cc2a176dc0d7e16f6d3781aebc684f13cc4fd81354a4dffff001d00caa9dc
+01000000f7b2f9d9a6f44012728b6e09c48750838780f1e1cac9cbaa09e011420000000014bdd0dbddd8e6c917324a49df6459de897031f514b77999c8476d287e47f23b84354a4dffff001d026719b9
+01000000a688cd51d1d4a0cf1b438ae4347bd2e0b07fef348ddeb6a4168f71e000000000e7da7a46f5efaf4fc835468b21620987dbdf8b5f66daa0ffefcca5b0cae2e533a8354a4dffff001d0017ef75
+0100000010f5b842b85241ab32f795ad605ee899389c64c77126503e8eba7f4d00000000df686a7f31c2c1de6a608553b26d6336434719fa45428eb3df59bbef75ce9e7ecf354a4dffff001d02e94089
+01000000ee47f9bf0d5d6d59bafc6ade45ae6da0702b2ae98b792f7a6b95d13f000000009c13741588ce9e729f1b29b82823a97da91a3c6bb89d03c9b33c6cda9d71f0aedc354a4dffff001d00ab6a3b
+01000000a0396d99b1042613554a8fe19f38a485daa2eb5b65f5347f3375a838000000008985dce081d487c7eb8f8b871738869922dd73120c75242333c45895bb91e64638364a4dffff001d04d09fc0
+010000000787ce301e6110773cfffc92ae93a3e017ce136ac2fbe2004bfd06ad0000000069ed55457af8174da637b03acdfcaaaa36cf822ec6e72145526cbfc03a6a83f546364a4dffff001d02fc547b
+01000000ae76485a78a7a225541cf8025578b9085d39b45d7c2f68669f05eb7c000000005ebf1334d253dd7a7ee381625bc200973e7af65a8a022de034d6955e37823c628b364a4dffff001d06e25145
+0100000057ccd09f8dfb8ff31d4ec9e743ce56ed5c5c501046d709f49c356f08000000003f818cdbb02b1a468aa23e9795f7518ca5a92276559fc40bca459a79e010bee2f7364a4dffff001d04a53656
+0100000042284cac669ab917936f5b96469048d55214c074eee3c7aec95a07f200000000334a4de0559a4db974ee451fe484a0376a44470a1495797992b9a6a0ec7b24cea5374a4dffff001d03a8bda4
+010000005d820c5c53908a13f372a860b57050b9c60ac3360a7bc865fec2029200000000d29036af962f1d725133c89721cc7dea6d3cffda3a529c82e39397027aad7efbce374a4dffff001d02146600
+010000003c3b2d4120567d650494757e86595b7e9c70223af627fa1857ae9cd000000000a4f56464ac27919b3cf8c9ae4390e679cbb2f878dd7098057431c0d92171fb29d7374a4dffff001d01dfdf26
+010000007e74fa0f82027865fdf1aa434e494c2a60e6a9e1b70d2f7c4fc21ae5000000008a360dec0defc12a33c3d959a7ed168aff8e34547db15b516e1b8cca754c0de543384a4dffff001d0568ea46
+010000004960b70d04579667192055ae5e019eae175dd65050e58be04d10b7f5000000002eb73ccd1a64ddd448405955586fe5c95bc54928eb34679f74bfb4c9da8e1b92aa384a4dffff001d00cfcabe
+010000008954c37766bb7293e105c476cf2b320cf9932b9543b1398823757c0400000000f85f4ed98adf68f14127aea968a338f2109597b484a808dfc8638112ad0623428f394a4dffff001d036b2da4
+0100000052d3ed4b6951d3d5db9100bf46b7dbfb177917539341431898a0aa5700000000d226fea91b99c5a31a034d340f647b722e50950c96a876eb96569efaeaf3b227a13a4a4dffff001d0051797f
+010000002d54616f93e70461538548ac861c860a8bb9077c3452be5b4c00d4c5000000003f88cda74f26fa4ceb0a97161e771a8b1f6caff1069f7b53ab49386916f3820b213b4a4dffff001d024510e3
+01000000e226b7f57fe7d32fc6166057172a4c2931bd4bc3b619a036df08c95a000000005b7df01117c335ca3e8a802376e171e537f090dbe2e3bd1fc291e45e219a8717323b4a4dffff001d05d6def5
+010000005412ebec87fb8c61287f1d2fa1c52617ded826adec22368b84c42bb5000000007992420cd0f0f24840c8d92a4a865e6c906e473c23f1cbe3c183de70c2b2bf42453b4a4dffff001d02a246ad
+01000000ba24ca40c17418d7bab5a3cac3c0bf00b9d3a0c09ec5771d0b14015c00000000a30e3f1429bf2a8ed14ca6ec9f3396b8593b8bb6ac1e4d35bd435f05058094ea983b4a4dffff001d02c75930
+010000006e823e852756b7106a0ba02b1b51045d87582001c006a2e8807cd612000000003c11b146d43fd62ec36b733942a52ba0c352c95a3f078808a38d080898cb8330dd3b4a4dffff001d053f9e6a
+010000003f03adc125b43a2c8cf9d47ac43dc81fddbcf12ffccc11ebb226520300000000b17c3beacfe667f4014c9da9057fdb6252978fa76dc4dc2d9ddef0562d293d39333c4a4dffff001d0467b375
+010000002bdffedf5912eb0e14baefa637c22cd523c8eb4147bb111db143ce9c00000000e24fd45c98a5b07ef9b236be48aaa1085b1bdf993a1c0db1dc04766895b8a2fb813c4a4dffff001d01c464fc
+0100000011c1b397772463dcb8430f661d548b3c55c61d415918681d6c4d4180000000008c32f04b4b70f43c849a8f424c3a82352cc0bc6bb25b2c2ec4a039964ea563c4893c4a4dffff001d05292e68
+01000000a0e705a21be059d1f8780cdcea04e7008b7497cfe8faa0fb59a666600000000059f060dbd892c1499758c7ce9bf8779d8e4dc298484ceeeca6467c17769fad5ccf3c4a4dffff001d013a65ce
+01000000492c5c9b8543822db5ad8694cce758fa2019bd5b6f12db4160bee95b00000000f640c60ea438dc020048599869836f5323ef47477ee17caddf076ed428898f71da3c4a4dffff001d0065ac66
+0100000095e3a7f3bdbcfd7a40901eefd95edb7d6d60562f6a427f7986d0861e0000000059ac3c37adfa89b9a907ef9d485c57262e9283e1eb96069c2de04369ef1b3c76e23c4a4dffff001d046e85da
+010000000a3f0effcb7af394aacaaa6e2383ec478034ae4ec4d488e47153d21000000000e25e353605728130ebf943b1f468937fc489589975c13765fd677e5050b487df2f3d4a4dffff001d00fe3805
+01000000a1f0d96d647241f42d4a3e02ea933690a4cc33542ed89de511c914c600000000153aef1fb8837f74a82054a0d9df9c566ea9d50df292ff62288082f311dd4212333d4a4dffff001d03d34187
+01000000f78b3189170c0de482943849707aa16247e5d62772953eb54bcc4d9a000000002183968b34446981f960895ed3713dd60fede3a9eeba1d40389123c6c409d3ba6a3e4a4dffff001d03d38a89
+0100000059835cec3351179502bdf8f4b0a0542d3ad336e22549c2a78f1af97d00000000f7f8e1a8f0b4bc0337d67fa4a2fceac7e83adda354d6fa3ba18dcf7e6fa2f2b2c43e4a4dffff001d01558b25
+01000000c6a2e4d395a1899b4be7d915c8dc660efc1298a34b66263044f5a5bf00000000e64b9c2a409a4a1310fb4b4f59559f79b15dc85e686eefc07b0b0a6b7fb6ee5bf03e4a4dffff001d05113e7f
+0100000067e62d9c29833a10aa99f00514ec678c06a234e05f5a1de4412988ce00000000e4a12fde56c03eea1acb5eb1b57d35ab1da6e55d544e8212c47ce277416b1b3e0e3f4a4dffff001d00652740
+01000000d468ac8c936b469fdbc0510d88d031ad3d22c3858abb8f89184f0b84000000000fa35a88c8fd0bf5897b921ac75fac304760f6d60ed2355cca82e689ebc0cd53513f4a4dffff001d0405baa5
+01000000d69dcd4cfb5a9ecda166293b3c0bd72e21a804b3a565c8ec4783f8e900000000b3ec99d78bf68d284104779999ff447ab7bc73a783e9fab814c6741e71849cf0d63f4a4dffff001d0355fe58
+01000000a4ee15d19e3a355d221af4e4d6b69a84510a60857ead0af7e3b086c1000000001c25b8c364fd6f8417d45b9501384908824493931b544baabf2299e25277305540404a4dffff001d052500d7
+01000000deea32ce4f7ba1bee123958ad36ad37df8d53372f518a88761d14ff600000000aaeef85b297622ecf311dc5d29669fd4e3863454c6feea361ece4f547ba52e6a96404a4dffff001d03a82644
+01000000b8751c32196c89a35575c5ea97c6395e6dde705aa771287c867b909200000000cad183a8636b222a8ab775d34658b1de92ed57818e26203ee43ffbd4d8bef95b28414a4dffff001d0587f431
+0100000035a68fc6ca182ccb6663ee8b7e44556b950a8351bf2c64cd75dc2d0400000000d59d2a49b1883c6f7ac68a9d2649dc0dde3f0205e19d8fdaf8065381f9ba61cccf414a4dffff001d0592553d
+010000002e5b592b34f37b1ae9fe17aa093d2d9c815aaafc5ce3dfb8b91e33d200000000a2990b3a24b766c99702c1d157925089bcd0d377cb70cacd30be33a3c90bd067e0414a4dffff001d03c217d4
+01000000773bd09b330364f704d0b3fa46d8431c5572a100faf117958fc15a3f00000000e739176d62b588566afa47f5fbbc0ec01aa3f058c036ca0ee8f3cd13e4223e01ed414a4dffff001d056b45a8
+01000000e9aa44db469453b2c657b1e4f3e29aba0532b1ba7ce4a8bd8dd00c4a00000000fa5f2cec2dd5e7122182c8e4b2f10a1cff77ce7972ceb53308b827aed06751fbf1424a4dffff001d00523f0e
+01000000337df8fe8603beee390360abeed7e956a280fda2dd5aa238e110de090000000071f85d3e358a18a3a3465a1bc93f17e7725549a9507d1b218dfe6d1dc349641e9d434a4dffff001d0042c372
+0100000048bb0f9a7dc5c4fc15e8a4c5ce0adefc49c10db0a582e0476c1498ea00000000640a330a8e9f920e7684276e987d7f5a682838ef8510fbfdc9de08474ed3e74ac5434a4dffff001d01a51ad1
+0100000098349ad38c19d6e481627c6523d9d4f52c031525b8660ed55c215679000000008c06f072a655daa64b9910b1894ca250077f930333c0281c843a1198a6a0924629444a4dffff001d02e40f45
+010000003ad363a33fab1606f7a32896d093b06b84a7d6e9784047cf8b9bc953000000003093ae1fa781fb09af9fe147aa05ed26e90f6e7312a448e934afcb4554d74c11b2444a4dffff001d0029912b
+010000006b8abc2fe01dc3a118f4d18c4f0b36b640fc85987db55437099aad3600000000f2e0926ac96d0c8010e095b34c5089f549f2fc8fa626e20b2f0b68dd46a72c2c06454a4dffff001d00149193
+01000000665559872e38dd76cd839459a94b0f6efeaded839d785b2c43c0be1900000000c305ed4fdbd3a11585c8805863472f53be7fb65b7b4404a688c070e8197bf40edc454a4dffff001d01b3084a
+010000008fc81523766707ea9cdc7926f5771527eb6a5e6810f86a836ec81d5900000000408efc695c947acb1c0239a9b011d77e55b702fe9c2f6082571ca3bd9ad46bccfb454a4dffff001d05bb7361
+01000000efcd6d1c267d0b764044e85d112b6a32cabb8698bd13b4e947c178ed00000000fc53d40de77f595e1419b205c2dbda50be0a5595c45e001f8dfeaa8d3f19d90f1e464a4dffff001d01fe7935
+01000000339acd071c0a09f678896b302b2c79833152e048f4536994737db5f5000000003c651e6ebe94d51b8d1d1542f3b43f0c26298e4e5c8fb3d275e4e13062800f6c9b464a4dffff001d05eb40cd
+01000000ce3f890a2580af6e15d0305dbf50d1fdce03678b626ef2c6f7be3c7a0000000092b0e20be269413c7f519a5e7fc316074aa24e930b8a3edb8a618077567aa832ee464a4dffff001d03713885
+0100000006f4241b0703e90950c5ec74c3e033c39e7b0c7b9b877d5515a6ef9600000000eceece737cbc2bfaba21cd1cd8515d614c2a39ea26d9b41b7daa77a17dd5ea5121474a4dffff001d0326e2f6
+01000000e65e080e4f1df42a905b85fd5d0f0b2d70f3e987e0fbba97cb41111900000000812ce3ea678b31aafecb99db5b0ca6718f9fb3474e0baed35aa6aa7290f5a40972474a4dffff001d043e313c
+01000000ef823ae34b33e5aaba6945f46e6c2bea1df86769d877ac0b8ddb0811000000001962162ca36701e79b5ee44367d46a8ad8421b1f80aad651a02022a7b9e1e0a3a2484a4dffff001d0259a126
+01000000c65b1537a27f39bbe1a2884f77807c958faa56f3819aa5e4d49472bf00000000ba78186dcdbed13712e6c29812a54fa620fca9ccf297b8219f7ca736e36e46b7df484a4dffff001d036752dc
+01000000d48b9919f537d161b9ba4404e9ba71bda419efe7173ec3569243364a00000000501790f1566c93f0edd817937da1e287147de7bd62629b65b395935017fdc80758494a4dffff001d023a2b4b
+0100000083aa1f8934efdf864a78a3594d5c16f7fc8303fa38c01cff7c0c94ec000000005f8a10b8f3b9a4f159bb071ed05f349849e16774cfd79990af242f0ff6a60ea5314a4a4dffff001d04ad807d
+01000000f699462ec6c1179023d1504bd5b9510cad887286984b783c943a33610000000074895c719bedafb41d7e5833189d876220fc5cdca55c6ad4b94a971a8e96a259374a4a4dffff001d002f84a0
+0100000077393e4b34076f0a6b6aa9350c799c7b32756a200eb1a7619e1b036f000000004c6b88dde02bd09335ea8bfb6db1e0094a58bdcb59d5ab78303034ff972b8538484a4a4dffff001d013ccb67
+010000004828e19e639deeb00d55f748421f46dc94eebd75e376104740535adf0000000014da74d7e2da39b2b2d676b957e3edc3619f4999922f3fa66c95b64be8fd92a34b4a4a4dffff001d0260001e
+0100000092f7a9432462b827705bf9b31224972d61cc9fa8d478939e015ce3540000000076cf4d344b2c5db45b55ea38207cc477342c74b993c401b753378509761729637c4a4a4dffff001d03396962
+01000000b87a944ec8bbd6a73f5a6054efcea696124dcfacb88876995bcff95d000000002dc8a455f4bd8723a5d58a675c5a5e833ee411490a443f6d47f08a6fbf7594a2024b4a4dffff001d03699294
+01000000bc2c1291105e0c6209906934c0f1fe57c4ad13dd74e43830b293c6ee00000000070ef89563a7d8b1c7232b9e391813476ce93c9160051ed55bd396c6747618ee364b4a4dffff001d03bbea80
+0100000062db800da374d1b83b1b460566da0a35d277bdec5a6ef743132b2499000000004af49eb22a467e87048f4625dc9021249c16561d372366a21e8c20cad2c65aaea44b4a4dffff001d0142e34c
+01000000e09e577320f539aef8178a3ec09dc306b301acf52e135839fb3fc55300000000734d92340bc4c287b0ef2b88001bac1f41bf5c6d3c725e0fc39e7c593d6ebd4f9f4d4a4dffff001d00bd8009
+01000000389998eeb4bc9b68abc63e8ed99218d00b857dcc5591966d46af4eb500000000752857853f92f5ca863f3254bf18ec5e3c62e1223c3f7968894048d40413daf3e64d4a4dffff001d00cc8cd4
+010000005faf1cf2ae1e1231a1b5aab191614ffeeec0ca86d86e105233c681230000000026dc493ba668f17fde5156a19662ca1a5bfd93b2376edf51ef9e3d623e4ef4a0f64d4a4dffff001d0183b4ab
+0100000005bf42fd63d8189e37f471cd3e8a411bf81b4ff8e65532897b755caf00000000e53a837e9e3c05a4b635531c91c39c3c45565dc75e413228dc93d0c764327f86154e4a4dffff001d036f8a98
+01000000dd081cffd3a2812f5c770b8e79aea5e63d67b972ebb5d9dfe8cea3a9000000000803c135f2c72cbd06b77cc2a2a13a3c063028bf4b188b4760bafa112e71e579f74e4a4dffff001d05fe4d4d
+01000000a9496d62f029c4b765548666b71a84adeeb9dd5674148b708b7aa70e00000000ad1924dbb0711c96b12ad1376816374008b98ffc43677b0990bca7973432f814334f4a4dffff001d0190dbb1
+01000000ffcc01d81e891bb248fda71e6338614dbea2d46b8e5f71460e6ae2aa000000005510b8c188460a26868d9bb3fc9bca7fca4d9b20f573e182466398f793d5ea8e44504a4dffff001d004f08f5
+010000004300ed388b39e36268421a7b15d337598edb40c8c290166b4376f2490000000054d4f73e569ddc7e67130ea0a43a352064bf3ded26f80877536b0e7277237a7857504a4dffff001d0268c158
+01000000d7e1a7d5fb8d460e90f62666eb9891f9fab06cc2c220899498cb01ec000000006a6a5be5a31aa8a7ced02f8ece18f44261e96ee9176b772fe654f1764cf6a4fd89504a4dffff001d02c96f9d
+0100000049599a3ddafecc620c9ad61cc4520a0c70fae3d34dec595669587d260000000018073767fa7a1ef0c4a570f373492a822b5fad5fe3092dde239bcd8081fd3e8c8d504a4dffff001d000e0681
+01000000eed0b252aae103849e6805f28147abd17f294fe55c5c2d966d8850a500000000e15a0907e27914f317962823d82981df6f2dbd256738860027c740ac3f842bcba6504a4dffff001d05ebd48f
+0100000031b485a9bc2b16f2ae5108ec786708f3f14447551e7f0f0a8575c75a00000000f6dc88e1c894cf88e930d30adcab0380af8fe00c3ebce9969c0e43242ca422b372514a4dffff001d0103697c
+01000000ed18faa5febd8be98c42fe2a1a5dc9e8ddb6a618d0a05d924c522b4500000000811cb2e938cc88f159a82f1e160d80f4c6b429ef51c63ac9f2724810ff7694880b524a4dffff001d0097c6fa
+010000008738d8fc729ed970f5223d77d8e776d5e0569ef2385a33596dcbc77e000000000bff98f2293caadd5f7fa74a7c5b3e2399268c171e5adb490d8e1dde1d4926d7a0524a4dffff001d02132278
+010000007ff2786f280df6f5039bfcdc1d2d18eab08e4120a5f67c769b8b464a0000000077b5007a1d08cd2a6abe2cb56a279ee4167092775aabf4245a55723455f7297059534a4dffff001d03c30540
+0100000002ddd5983c40e70e36643ebbe8567a8c115dd7a21262b8c61d693acd00000000504305d5fc375c8303ca009ce2bb783798b303208c8744fce95d0656375b682ab0544a4dffff001d0483c2bf
+01000000bc5ffef87baf263ab01ed870a064c46731fdb99beb941bb6ab73fb05000000001679c9d394f4c959d3fa9e054a4e89af624f9206a777368a00d1cb6b175ac5f7ce544a4dffff001d02c1d1b7
+01000000a0fa21e1683774f057767d4545610a63a4c0ffdb774a6577224116cb0000000025db6d92e12ad8376e7e37c12b541faf358404c4b5617b9b287a43c2db4a067b6f554a4dffff001d04cfae4f
+010000003abf3b8f61bdfc588bb0440b0f61cc8894cee1291298996712b3e3c8000000009160b7843ed42d0b5ba734634abb2dbb231e68bc37a1d508b2f27ad88d040e3e5a564a4dffff001d00028074
+01000000f23f3229508a7b80a755b69ab0dabb405f5eb566419eb60e2cc1ce780000000015819a3494e7b49042529989b007e8f89a6f69f6d22acd6f0132c270923a192eb9564a4dffff001d05ecd17d
+010000005fc1bed214932ceb26a0a81a4dcc6fe290e5b7fbcdbc490f89c310e900000000c6fe4434b767859a8b403df5f195e223e622058ea98abcb0517bec7c5a734fd4c4564a4dffff001d030425fe
+01000000390b6d99e31e1f64e6c16a94e7fed310d80bcccf9c7f9e82fd8bb1120000000012d6f8906065837ccd176ea73171077e73a29257d7e0309377e710ebb5629009fe564a4dffff001d0391c3f4
+01000000005ca35550b9d7bb3026a8a9e5c512e0c4714f11e3066f99be7323100000000045ab4575f33ef6bfc4b1c9fce77ea21cb9b606acf2b9a0331b8f9f931169fbbc64584a4dffff001d03f6a4c3
+010000007f0ab1ef14e058aa9823bc23ecaefb694cabcc7de8ca93966dc1cdd300000000272ef40d0354ece93dda9c8370dbaf29c6a2894fecd84a063c5d3de0ba90a088de584a4dffff001d049fcb9e
+01000000f6707f93948703990287c88f79c47c2171dbfa2890d69c742ca6dca5000000001f75bdfb31993e02ff9de178dd3f1d765fd7bfb4dc87280c40f21270d7384df6fc584a4dffff001d00b0d294
+01000000096d0d56aa45bfdc9561a804aebf045d86dbe4538ba8abfc483800a9000000007a22463cae54d2b07238b58272c2f47c7bbc3bfc92a15d836f014d34160dc3c10f594a4dffff001d03465905
+01000000edc840e0c2f736161507190b975b87e70ee7842a0449b1bc785975070000000073998e2bd1d6f08ceccde03077d9d86adda70cd56234563c8d37e275767765de74594a4dffff001d03c9895c
+01000000fa8f5340eb43544d2562339f1854add2ca2336be9ef3f1659cd2b3c300000000048d698b6d1dbdaecb6b5fa3cdf3c0b06346e43c2aa592606736404a17c7080af85a4a4dffff001d02cc3a19
+01000000683e618822053eeeb0d0a85cb955b3eff25df82e41beb94adae2e81600000000ef84f5c1c80ce84101f205ed007bef67688a709a1638b20bdec8e2d5404820f6cd5b4a4dffff001d0004d110
+010000001a4ac80ae8fba6ec44d500b823515af90fc24eae8aed36d88f73b39100000000d20b7be0fb4d88ad841c2d111b49b33e7d3b786ab431f3b32e6a75d2cb8299bfcf5c4a4dffff001d02b5e1c6
+010000005d8227de291d61223a71e9c11f27298dfda9b82538dc67ecfbbae9b800000000f75f109d50bb4674cee3023c0780403b4998067852cdb56881e69c4412c445b2485d4a4dffff001d00bd7afd
+01000000140bf311410ecb5324301a67d5303bf817dc0401cb674e1c9d2b05e600000000cfd1b67825492351322e5e64da5db259d9eb1fa2b309a9917c921685232c9435775d4a4dffff001d03dbbfd3
+01000000a51f6b0e1785a625b71505e4c9ffe60acbae34d5a8e463376b0dd37100000000124c2dfbc87a28fbf04646cef5ecc103f568b8d28ab19661327995a7389d7325c05d4a4dffff001d003facb6
+0100000017b0e14d03cd3cfd82a7e8d54fdf338b7679ffc731ee9eece8d10b3500000000c96f851c0cf08646a15dfc49640c994343795b9609e87350e83f7c69fed804eecb5d4a4dffff001d0180f4c6
+010000007efda66c68e131be2f78ac7331d1d50f0203205a1532e96bebf3c3a10000000075eebc78e8a107eeb04c122618d963c038a00c956a67fe0c4933351c92f68def235e4a4dffff001d03a98d25
+01000000528b90ace50435d962ef0e1457a28b1c140e887060f6b277b37211de00000000739c079a15781e97e60a15e05212ca04c97d6c7509737d5daacdcc61982672e8355e4a4dffff001d053ea865
+010000005533106eb5b75af3ca4e3fb1196fa4c0d4e22413617c385e4ae0984d00000000a6b546b2b262c57c6691555d6f2720383c481ad64f7525c0c266669014632fa2ab5e4a4dffff001d01af78b0
+01000000c6d164638ea706719ccf9a75c7b1cba7a4ab982815490ddc3b2567fb00000000ba99c12105363f09230c6331157d8cd67336745aa9e0ecc46494634e26f875439e604a4dffff001d012bdbc1
+01000000364f362c384a75bde3177d475a8c85340d4fd4ab0ed0b30f17c3317400000000df6d00f0aa27587fab36da531d0c21a27e8b9ba3e7be77a780dc8277fdadad078e614a4dffff001d04ab300f
+010000006fe6f7e2e0d1df2b6d71c090c4fa296a2d7d175c9b7853851d0fb03500000000610e56ccf020380fa1918b77eae3eab93231c4bb8d741efdae7c5256d7f01604d9614a4dffff001d032e0df9
+010000009f469645bedab4134a07b9bf6108960de1a2ddac68d59eef58b493a400000000faff99059c41eaf7d7ebdd38efd9b0e5a3ceb2ecdbf2df4b266f4bab573363371a624a4dffff001d024ad9f7
+01000000d4767418fc518c1dad34cddcdbaf06e38a73f680556295a88bae600100000000be0a584aa45ca81a0ec8e4943b91a7e3cd4b33fa67da59db3a15979326eb5e0973634a4dffff001d05e4339e
+010000000bcd63dc71092a95a3cfa24deb7a99c097b5f74ee21389f4cf2cec0600000000c590cf15019323e3eef49355d341374d4eb747c85118d2af7ccf749bd6322d81c6634a4dffff001d0453fbbc
+01000000e244d491df952155b701e23db092c318752154d0c94b7f1918c139b800000000a7f1371e40c1c7479a0182184e7858b9fc654b553014683443fba375927f9d4f36654a4dffff001d03e0298c
+0100000028836ee71421916bfbb216200b5943426946b65d28ead2f6f6b639f3000000000494ca74bd3ac3a44c3e8d2bad9337945f89e45eb63fee5b9860d46a7d4c9d893b654a4dffff001d00ef98f7
+0100000017116f1db86d264512d1e919a74da904290965abc0492169532572230000000060081ea832d5c3e9f675736cf23993c36e8eecdde049a44fcf955ea24e7e9366c0654a4dffff001d034412fb
+01000000e79cb282d213491706610e7fc8b02ea62a98e437829b076db087e081000000006fd1b2d70cc93f628dc0f1ec50b4fa820007225cf827e3f94aa581c30f5ae51528684a4dffff001d02c8f506
+010000009539a568fa254f13b10401a64dbf10d2709111822efb587824955f8a00000000a4cc887854ee5ae17aee01ff839bc7cf869aaea30fef9c8782eecf239da4e83f30684a4dffff001d03f4d7dc
+01000000b021d8608d5c1517ed201830c7ab24f957ae2d71794ff149c7d11af300000000e58177c0a9f4858d209aa9008b6fae86b6b436c84c906e5faae402e0b14093cb39684a4dffff001d01e43108
+01000000b9d79c9f24321d231a894313febb7c5c264796a1bf60815a0cbcc96a00000000951b66c72f193f3b8374fcf39baa8183795f647fa69a5b2e21ee6505ca3ffe2ba2684a4dffff001d016977c7
+0100000083cbbadd846fd2fca57ab49a7f4b5ad6b1b64889b046ecc00424fe15000000002e26a89403d2d4d2c0cd13b959f9b86c6b6baee4ad0d99e6d04ce21995039981ad684a4dffff001d05ba9aa4
+01000000e435195dc29f89337100e0c2e80944137d2ca37e8e00256fad8b3bf200000000a644a58880e151d73b166f2ee7264025292968dfc2ac6b19516b9fea051a001b3d694a4dffff001d00a59466
+01000000fc27d9c3e7632342b188f423df64d41d8b8ca4c75b7d8a47fadfb13400000000f092bd85e17e84ee69919ffaa3a1936fa24ced5b3d0d0ca073398a256c5293dd4c694a4dffff001d004914dc
+010000001dc0cbac620aa5469e889e5709b5ffd3dde92cce25715e084d2265d000000000bb549c5ad4bbfcde450dd1998da568ba168ce940479f92d795235e95ffaff538c4694a4dffff001d01cf90a7
+01000000403be502a01dc5d45288d1f305acdc9e15cdad638ad918392738320300000000cb513c7878b4d2c2376dd1a8d91aafcbfce7fb3ebe6bf419ad69e7408d8e620cf2694a4dffff001d0000764d
+01000000919b3148b047d8639e8b46bcc4845d91654c03c6249ea4c443e9b99d0000000061a3e0d8305112ea97d9a2c29b258bd047cf7169c70b4136ba66feffee680f03b56a4a4dffff001d03c843b3
+01000000f90755f869f150d7c4a7191ffb3f74cc1752d0e1c8fa6ea51ed182cc00000000cbdf8c487a3679669f416aa49e20d8665e75ad2895d1256589b996a7f57fdfc6f76a4a4dffff001d003cdf61
+0100000047b42b4f634f26c6344841cba855c2cd7ad377cb35a8774e5e7f58d000000000cf16ae92ffab695258610ee3db7e1681447afcfe60c28ab3ae30e3ecc06de718036c4a4dffff001d036703df
+010000009c57f3083374e7552e56d053b0f07fe93a47f6a30430fd36eb40b5250000000017eec598976bfbc55ab4c5d48e00162f31b6aca31bc42ab7084f1f139049b2c60d6c4a4dffff001d03b92cbe
+0100000087de9b5ae134e6f3c94361941a079bc41b84ccd02a3416e43369ebeb00000000a0fed8fa4afb32368c57427cc0d9b44869fe8faee44f71910e9408636b82f3b2166c4a4dffff001d02379365
+010000000d544d94180289b766a4cf81e3ce5299f938c7ca3f6956424558ea700000000016164d5a284a0b308928c567c1db87c566e87d1f4810489c8256edb290b21512776c4a4dffff001d003abad2
+01000000555e499668283fc21cbbf2d5d911334ef50c6a58264fe4a9f1c7406b0000000042bf33d98dc089614e3ce83d39736949489ae64bdd1fc477f451c2b6b523c8b88e6c4a4dffff001d0210c9fa
+01000000fba43e3e3dda545e1f21d1743123d5b717b0ca9c3b6bab06ee978fc6000000007b3e06d1951308c10625b2c7eeb400f1d9de694922b34bc48bc31cc57945ed609f6c4a4dffff001d0256b7c5
+0100000046b8f387b47ed742c035e8619551c1ed480d111d4e8d8fb231265fb400000000ebe3b715280fa9cb3a6de1d7ed2b27c7f769fa8db53956a443bf9fb8051c96502c6d4a4dffff001d0587e243
+010000009987e9a48351803b25ab0eb6b84ef62d92e33a73b2133687e126c09d000000001460afd3c98403c75ab5858f765e6bdc9a91a9a7377d64f262b0c4d4764fea93936d4a4dffff001d04816e32
+01000000f38c31b10930176f539d4c5c54f22a4e65bdf568ca89ca65518207d300000000ca13d344cf8d51261573a71698c8fe10b7e5d9f7134ae4e60682ff793b1b0278cd6d4a4dffff001d028a436a
+01000000f0cc611660dbbddda4cc05700f1ceaa209e1d9e166117e5a4c2748670000000014f341b19c7417fc5b4231ae70027556174a78af7e554174541c36e2e1af9265e96d4a4dffff001d00d47799
+010000003f732830a72d4d1043e43e627abb770e9586c38b530c03331655c0b400000000a28bc5d7b507b81bd9208b9e304460a400dab435a53afd843782a9f9e7ef0b076e6e4a4dffff001d006eba5c
+010000001c219fb9bf6eab86c72b3d3bc789b446d5f214222d91c752c4cfdbdb00000000a62967c97b01f47d3a252fd612e772eba117edc1f6033fafae4fa66eabcefcc7096f4a4dffff001d019c3e0f
+0100000006de9b309fdeb6361b60029f1c7d6de6c99ff2819609a8c236d9feb300000000cbac2ac965c6b2ea471a630cc99ffa835915fd914c107b2db2dae5050b2ea9f36b6f4a4dffff001d05df3994
+010000002b59084f6b07d1210181294223741d72117914a497e7a07d0c730d4000000000b3ff096b0e79008a55e1816779ba4811e3ff6f3a064b6cf84bb4b1dcc433890cbf6f4a4dffff001d04090899
+010000007001e49d2d872afdb4cc1836b2d91ea483fc7aca9abed6e2e78a8438000000007444e881fd0c51e363543a35b27effae3cff02aa828a8de3c37bdc2db2b090bcc46f4a4dffff001d016b52c7
+01000000d5c3785332c02c192281491fb6b1af615bee7e2a2fcbbc124ba72e8700000000787f53156727d75d5b5a8d88aa93400ca4d53b9d90ac33da81545750405cedd3dc6f4a4dffff001d034694bb
+010000009309d876716d2ead60108db4f93782659182759f0b351cd92ba9644900000000fd05849b870af4a38ba121721850a8cd344ca95c3aef88c1ccf25288776264d0b6704a4dffff001d00d1727e
+01000000e9206fbdca986700b3747c62ab8169e60181ffdd3f5ca43923ffe8b900000000e08afbe6f2f42beeb5891d78d6774548971dcc9b3b7d3407821e3df29fa1aa0fc1704a4dffff001d045d66ba
+01000000bc31e83685c5e76b127e81f12e8867ab8342c02ca51f137a6abed1b3000000001e6e383300d86f0ab1c84d68a38e3a64f94eda07cb5d7b6b20072cd5a610cdaa42714a4dffff001d00cd948d
+01000000048a5ab82d24f3dd8bcf472d9081d4aaf4f16a16113821e0ec9d244000000000c1f04bbd361fc5125d1721c032aa88337bfdd16267b16498a8f80f00e2d3ab23cd724a4dffff001d02989202
+01000000e891684a9cdf9608002bb1861338f42e0a42ab96d9656f9f40edd3280000000055248a96fb577bd5c2c929f6b0c5757284ebdd13d09200fd4c342604ea3e5ccb7e734a4dffff001d031094b0
+01000000904984b520e8c777654412655520279fad1f546529d4b255136e42430000000035c50c2c01355804c412bece60540459c86d4a93f336396a664e88acd642347600744a4dffff001d017ae930
+01000000a31367e28327fdcc4b9c28664e5aceff0c6439f86378f8d88a42017200000000c26f017904f09d6c5cdd1b33194d0505a594a6b37ce0a7635ba56ad35e63183f14744a4dffff001d023e46b9
+01000000e78b13a567fa3b7f4329ea4a3700acba52539ea92128bd929e05853000000000980fb406fb9cd2562abbcbd3d2a9698da8aeb255af37c1237a552dbdddabc17d39744a4dffff001d057ca591
+0100000085f94fc42a95c9958bc3e963daf2c1550d97ca945988180a9fa276d100000000931a84625efbff3f1fa30974dc3b58d89a3a6c8c71b673e6a9b84e659fd657d3dc744a4dffff001d05acf2dd
+01000000877bc8f841c9fc5f943d51655b089a4d7f545cf233f7b3289d6b113600000000d9df6431534132d6fa686d7496a4825304f8aca55654260c56d4edeaa4ee7bbae2744a4dffff001d05c15bd3
+010000006730e2f111da5208e30dc182cc59c9318675812c24b596e3e5398a4d0000000092c49fe9e2c4008f430cb72949871a174abca604b3e4731d51dc02337cda177ea2754a4dffff001d059577de
+01000000a924ed313100b3de054fcb9e9748a12692e0f66d375cc05e01f163760000000039cbb3487e69c00c58056f6cbb3d5a3c136effa76d0d9f0df9fa8963ac0a197bd0754a4dffff001d050295f7
+010000009d977cb94b7caab58fbe8bfb6339a7bc09dc50a8aed9dec7a624e25000000000a1275135d09b38392ea718c862397f389bd358fc242242072db18a35a211ca93fb754a4dffff001d025f7654
+01000000f03542a950904c6e1ff7e8ddcd6c53a9438000df6d3846300afe005a00000000ba9ebb9774f0bacb321c7960e0e13443d58270ddc5493313d96c7784c47f1ae4ff754a4dffff001d042989ea
+0100000092f5763855630332ff4790e27b1a8a37975899d435190bb50f8448d800000000c0e15d72865802279f4f5cd13fc86749ce27aac9fd4ba5a8b57c973a82d04a017d764a4dffff001d03bc04b5
+01000000b2dd87c5ae6f6368f5c00991d7d900c14441b315e0fe3cce598cf0a8000000003e0462da7bb4bbbf44948d8cb4af46d7fb0017f598329f2139f52c76a433ff819a764a4dffff001d01486ffc
+010000009369370b1d364678e42fbb797a4ee0121fbc3916e5cffbfc5a8e1a620000000031774efc0e33444c762baf022787fbede880fdf466dfd754e3e457f4afe839b32a774a4dffff001d043d36f6
+01000000514da16d5dd2a4f82d4e52941092e8508c4cb7339e4895eb255cd87d00000000b6b3d4911dd13b8bb3d06b75eefd834250ee263cec3e94d9b4c93f600e1de90f5f774a4dffff001d01e3c40d
+01000000f3a31ef7b1009ab512e3f3f03860dec8a09c3e7ec246e75163708bd80000000035f2a99aa12f8fa65fa0ec244ebc42cb7161e80bfcbfb1bf3bd89c3585368fe504784a4dffff001d01275ec4
+010000003526d4c92a0e4a1ef78043d2832b94108ae0d3dd21f7738e2033f3c700000000a5ce8a4c43d3b59b6fdc38416eff39c2d1068817022c79a4426b071b85a30cfdb9784a4dffff001d0052c6fc
+01000000875794831d6f2c236759cca424704c18b8d89ef9f39347aa4b4fe46800000000dd4ed8001a3ef95027770fea474b5e63fefb27ec2f6b8648865f15e69308ff8734794a4dffff001d004298dc
+01000000fb0922edf49601327afef737beb3d683f98b19d128f1ae64725b5abb00000000bf3d088f283f962487807e618960923be192a236beef35f4392d7ecfeb6832ed38794a4dffff001d049e4ba7
+01000000046dac6511ee9b24b34cc63cd704497d6383d1794cc058b28badd5d300000000e885af2ab2be11c610d75db7eb1bd6eaea463efae811fec483ffa41aa7e83f8b687b4a4dffff001d04f872ed
+01000000a658975dbe7c08a66b40f3a2e2f0ef98a255d60a79ba975b89a06204000000001e7d0335d1b38ddfa9aa6303ef27c0c6929af97154ab2062f3934a8a29b78a58137c4a4dffff001d04ecec44
+010000008c36b480e7358039d56678918d633d9e92a8f3bca09cfff8286e3fa60000000013ac254600fca551cfb3665dc3beaf8114aec034861d36308d5e6b213f7c01c5a57c4a4dffff001d0061a279
+01000000169009ccd47ab6783ed3657ecb5c962b3b3d3d0edad231df0c9f193700000000623d61fe4724621efeddafcda266ef8edb29c8f2ae051f4c6cf6eb2edd214e5ca87c4a4dffff001d045f755f
+010000000b89ccf6b22dc7ff1668cd3de5d6a812f1e5ef46b512cc9e352f3f3b000000007c644560629caa21e1a3fb2bc113deb42f78541283833c4a3abbe6a6fb6d877dd17c4a4dffff001d0588650d
+010000003f9e8ff2fbb3e04c36c9683dd534a2d4fee5607825e10a2b4885e8e90000000034dbb7fb145e4c0ef8b98d4a03758ad3e9fde5016b147095494046dade14e834df7c4a4dffff001d0320c105
+010000004ee7497463e160c70196b1412a2ccbd2cc41062a773c4a3202b7b047000000007cd53c97e3e7d4f9f8e172a6168aee8539dcf2f4aa98caefe37be637000aaff9fc7c4a4dffff001d04b19e89
+01000000999ad8365c277b20b05558240e3c383ebea0d478faa53122dfa506b50000000088b03731e11466e46268a2473feb0bfb996a77f1d39d1ad9a23d27aa158ef8719b7d4a4dffff001d041a251c
+01000000864569f3105e048d65a9151047f0ca03a5c3311c39dfd84c35bc279b00000000b0c3d72e0b5e89d3cdd7b5d44396e6a4ef92f4a2e7e198a6ee583768d86c839fd47d4a4dffff001d0348f715
+01000000783b15ae83eabb19aac502a6fbae8c6b4b2d8dff30a1c3cb2af0b318000000005e2072b31fecdf48b38a60a1613a73f8c370cb4a857c74844ae17ae30adc9677ca7e4a4dffff001d05a98d10
+010000009b974d3c625f7fcd4e18d7b8e5d53ec6db57b0987f7ceef46e9175c20000000005c8b8ec09654909eb66cb5d6f22c7ba56afafbd3e6486fe8885c7f0ef53b77bd27e4a4dffff001d03cabe9d
+01000000d04963c151d6298a97a6ad4c762ccbacec3dabe179b3c39ab96eafed0000000004aa8cff9a7ef9a2029c8f3d45561888c3c50d9d7e9610de7c7976f159bd0d55ff7e4a4dffff001d02e2bf3e
+0100000032b6290c64c98539ce9570e69dec2e354bce3909fb62badb2c6dbbdc0000000027cb02841e5e22c93f1e2b61c1cb40553e97205e0956a3b01777c137d3264cdb107f4a4dffff001d03e0d16d
+01000000b96ea7181758239d2d12e3c43cb81c47110526fe397595956ec24e1800000000e89deade7ce10d96cd17b1371d1bd50a16e04c997331c3a49baa0ba0cf84e6c5de7f4a4dffff001d02ea02c6
+01000000c80ecafc4bca833c5f2b956f4acd585becd120bc9712517eca873bd5000000003c4a3ed10ee0ff614113e34850ec14018c7286ba1868f4eb541ba0c68a0db05def7f4a4dffff001d05c8b8b2
+01000000c451a1fcaa893e25e72b921e74bbbe82e4489aaeff82806bae8e2ff3000000004d8c8758514bb308bd043ba7c6ab04555831f523ef439b44b6c2d26755a110b86e804a4dffff001d01868381
+01000000b8d4747b5c3a2d6c8e50e389fc938ef7b8c04eb4104c17a2e2694f6000000000aed381b06d2dfe28d0ce14b4f43f5c167dcfbc80d44e37054a014a828a2c8c7236814a4dffff001d05517119
+01000000d0a1893dfc1470c05078598f83a28af1e44df83621ef6a34319f1f79000000008c68d3c55f59e4264a26c2cd1a6a3ed4d45c98eefd14bbfb9a26cf55ba30611d2b824a4dffff001d038d2dc8
+01000000d892825bf696f9c10eead1e3e97dfd043618e0123b72dd058988f92a000000007b921b39ee758310c934e9fc074942513e85716327fa08526e089895530fe6bbca824a4dffff001d0276d82a
+0100000062f08100a53cfee15d6960d2915fecda72ac40a116600d176bd6eb5a000000007d4eefd21df4c8472009c501a9c023613b9b67c27231f130cfa72d97978ae996d2824a4dffff001d04f2f222
+01000000c5b9489065fa7e1ac4facc51a5a0ccc2111911609f43386ebe7ca1d200000000a0db3bbb22a2a8441d84dbe335c24959ea3d3d6e91bf67e66bbcb0d7e0a9c4836a834a4dffff001d04181366
+010000007c967c39b155d44a57d37c46bcb47506c0b00a7987d9debe642c4c1a000000009969fab4c50985190c20867d5eb2622f8994a72c05fb9c91ab057be79a80526d94834a4dffff001d0391a66f
+0100000015276dffb7a0360993a5a83751944fc88a2d3f13f8e9363e5f3cdce300000000cdaef86e7120eb9f7e0e9c1b3009b4581d1133e5e2371f0da2a0b7d314465f8dd8834a4dffff001d04184062
+01000000d9e737d6b012027382d48297bfa52d08eac8ff7aac62810a3bc6798900000000be2b0f66f65fd65f4d4e387b96041ee0aeadeb736b467f8b64e12663a7f8b92971844a4dffff001d016cbc40
+010000002059d85f0b361f764b21d2254602420fbd84fff571daed5304862a42000000000c0bdb3ed18e03a8b3b92ee43926dfcab9a2bc048c98afa86c1f24e1f7f1ab7e23894a4dffff001d03dc41a4
+01000000c34c5ce16980a5ee4c66a17ca4d8619600a15f7e0dff5edec12209ae00000000e7be4fb74031df2ddaab02750360d6b806cfb54cfb9519ee517c20fd9c636b2332894a4dffff001d05c8f18a
+0100000071f7cdf7526fe514f6a17de62bfd240d2eb1fd5f8e406da3833394490000000025156dd8c9381fef3564ec230644a84f35524ee664c3643ccec644088b95d86f7c8a4a4dffff001d054f7dff
+010000005b09986f4fe70f0f864a258266fc73e908e170c29b3cfcac9ca9fbe800000000e0f8760379c70b7d7904b5a50c59bc16afc4529114d9b9ac1de43e698debefdba18b4a4dffff001d04457234
+0100000067391d93a161d82b8f968b7041a48507ace6bb30d4dfcc789e0b6f7b00000000a035032fecaa07e192fa2c97f683fc201a50f90c7ce4abcc33ee37aa528503e60b8c4a4dffff001d02bc415c
+01000000ddb642656ce71cdf2052b12984eb8c297eed7803ebf000dc63a262ce00000000b9bb4103d0d93289e9f9448ed7e63a8ac751ceaa41d1cc9627d2bca7d7c28a16598c4a4dffff001d02242de7
+010000005705c3df459247a82aaa9c3d836ee2661cfe00c826faa3f2a8462222000000002204f4022b778f21a952719904fcddbefad0afb48be8305e89ddaac4ec8b133c8f8c4a4dffff001d01ef8ecc
+01000000560f99e148e5e82dc838535b9cbb5a6116eae26b586531fc2e2a8315000000007760f7a3f07d88e8c7361e1223ea4a9e4fc5f9d8c421037578ac06d921f11095528f4a4dffff001d0392d460
+01000000cb6dafa616960c0d1b87c67ce99dd7238f3891d280444d7a15099f710000000090adecefcdd85452dce9b830a3ceb9240da10806a62404f8b7048c2d85ea0f7d818f4a4dffff001d0331ed48
+010000008f7e1c2ed57b82e56977639379c6cc7eaccdfde5f181fa381b0495610000000023130ef1dba152270b2153aefbb5d4e29c22be3e79b2041ca20620c1b381e099ce8f4a4dffff001d05e4da4e
+0100000005b132a4f74a8799a57a4202d0eeb09612cc08d295401f007c4530dd0000000073340035d03933e01bff3c47f14a5b0a8ceae33be12a8dd521315628ebf42eb3f98f4a4dffff001d003a7958
+010000004e6790a025117e8aa81fd453c7c6c236da838f3dacb169abe51897d500000000091aa6ac0aa796201482553ebe24961fdb79bcb2c1b0902f2ecd2c9e7c705d830e934a4dffff001d001badf1
+01000000ef72b16c3d1e58804b715c8ca9d02f2158524171a8a4742d0a07974900000000ecb21277a56d483af3a7ea1615a8a6d0566bf87ef230146ed4a8cc8fbae417b2fc934a4dffff001d0370ca3b
+010000004fca0bc6408f652b0d9f79713e880890605c993fe954bb73808f2cba00000000a70a8a17b106c669acb346ac08ca99344b307121658cb8cab8d00eba9adf7c9138944a4dffff001d0328fe63
+01000000077ee2849664864a66985199aa49b030f66c79f8022116c979e234b30000000087be9e1e4f0bda5f90e3c1db350737e4f390064d8aec43d6e00ea92a27c2957ee4944a4dffff001d03af3fb8
+010000008b0d4a2ecef90647f9d1a923020adf2bc138626877f0f855fde5000a00000000ecaa67add6b1f10ff91e5df491b59ab1594c357b39ba9f1f06022b078844375e99944a4dffff001d0037c556
+010000000bafbf706c6cdcd91f5fd082255862927a4d3a0457f7d7fcc6ff3e7600000000087028b6299590346eafb168c13132979b89b4912750975d9bac131a7d4c68c3e2964a4dffff001d0525ab4d
+0100000025d15b892b9a091f18e865d579f536c655f18a4994e960b06227a643000000005a2a640ca55a1660daf5363b670daa00560628bc3c39a6dc88d7d59cf2dcb669d0974a4dffff001d0226a908
+010000006843e8fc9750cbf5174a0a778ae5a6a63186683d1296a8514359770d00000000c5eb73e7834883d87b8c0ceef4ba9b850fe503039eee9b28bad767afdf0d0416d7984a4dffff001d05d4fe47
+01000000e3756d4e486befea24a302f095aa87c984f583a66687fae68d92ed1c00000000549848fd7d128b20aae864145aa351ba58eee3feffd7a8089c7adf4445b8de874b994a4dffff001d0496907a
+01000000cb8461389ac8693f9f277f037cec93d37eb8b6a6558cbe10bdacd1c3000000008aa6ad7fa12e8ce311c1c659166e587c880e3a6fcdae871eed1bb350d713f25113994a4dffff001d04744479
+010000005573c2792f3790bb99c05da5d71c9a24bce92ffeec84093243a1aeae000000005945dda5801905c0a507e521331b06a9263279c4761dfcdaef431c0a76698688b4994a4dffff001d0546400c
+01000000afbc89759982446b305b9f48cdb547f3c95760014fe738ec3e32dc8e00000000fea6f2fb7688cb32351b15857d3f250e581af5b20d9379070b5542a00c42ca70699a4a4dffff001d00ac7c0f
+01000000e17f97a9d791f0724271f652f057075f27d96aeb365838d8ab17191500000000a91c00538ff6917dce0bf745e5a040479862ef7134de60320297eb029f7bc07f469a4a4dffff001d03b1cae3
+0100000050e436836fe519456bd7faabec5d522aa80bca6a53af3f9d57457c4f00000000c66962dac9d02aab13c8501636c123f672ab771c8e088e1b24275b105d222c7e569a4a4dffff001d02c1415f
+01000000dd654b9a8a51371c852e448ca417a3fb05413a70672783ecb1346f640000000044fae19fa72a7abcbf78d966a5097415b961a316322184bce818191677a3f129fc9a4a4dffff001d01c6aeea
+010000004386a29fd8c55a0c47b8e97c6d04e1267549de89abb6db75a577d6ee00000000fa993f2f31763c2969af7e8c181a32cb663e07bd210ec14e29fff768621d4b58459a4a4dffff001d03c6039f
+01000000c5cb299e1b345b77d730c527034828c846f23bd3630ff74355c2d9ed0000000056ad317661356b368d8589f343000fb68353f92172d8c7f68a9c9792cc94ca5c779a4a4dffff001d0485feb1
+0100000028fd73f5c4415839b41e887ecd606661462ad914da28c2140afdedd100000000af9d78520fff7f6212f88c7c98521e2c9a8d2a23740ed2068e1b2a55e0c7aba71d9b4a4dffff001d03a07cff
+010000000cb32bab66fb7c93a66f841608c74f61dc36d662cb01f30a8f01efd400000000f8bef1ce0194b9aee865355e8e5b0881092bb9fe8e3842c4f7110f7306caec26629b4a4dffff001d004d55fa
+01000000c2a8b9e3677de7a6f7286c8a458cd5f09409c254209d9d8c445e0d24000000006bf3416c661bd39443f16a48fb560e97558e2cdab18da1e598086cd40a2ac6ca379b4a4dffff001d02c8a75b
+01000000653dea66e42fd4d1019adec51e1f7493b06f458f960a6b08501d8534000000009ec37ca4395220c81233283dfbe020c1982b43c3cec6b82ff425173ef558a51a5c9b4a4dffff001d05681cfd
+01000000443c685dd9cd61dbb1d4c1964e8eef7a1e6c6688b38775c2707b2f2e00000000cfb0c5cc863b3809bb9c642a497ab8eb5a2b0e6cb448fb1a12b08c3d0f3b1e13989c4a4dffff001d04a008e2
+0100000059ded6607bd4ccfeb4348c41b04a13343602879158b8d9aa1f3acc6b00000000af43000d9c2194875f48dbe377b839bfa2157758b941b5020e58b55e2ed04fb5de9b4a4dffff001d05540f10
+01000000d04014b56c58d45f823589d9d36bb755c50ee96359ff44449d71cfc4000000009d6e5c4cadaa256c698491f2c3086ceafc5f73eda5e54d4ebd50ac8ed90eefe0c79c4a4dffff001d015a6219
+0100000098fbe05200b867e000286338ee405f70ce4362f54769db2bc9d919db0000000078b787d3d8da61dce5a9fe4d4a9232e804021e439906e6fd52cf8f041f948b3d919d4a4dffff001d05774a61
+01000000c5f311edc79caeab3404ee358871e5f886712e56005f4c67141dadd7000000005545fe6585b08eceabf29cb4ff68ba07020fb36520301d4d5e7f30dd1489aa6e849d4a4dffff001d0505dfe1
+01000000f24700786a174a784f2d99ad24d4c0165e2f64acdf0893cd4839590a0000000022c09454ffd3a434342bed475ee4e9f7a34b99d26b65f2ab92bcf7418e77f732e69d4a4dffff001d04872d33
+010000009849d7f7915299b00804b04d89a946033b4bac82b431a04781ee1c6a000000006801e653da56cf733fbab3ebb4d3f248ae5066f00b0b879a178ce3522c7e1572cb9e4a4dffff001d01e83192
+010000002cc41fc32ae1712dbf174b8d810498ea52c9908a96100cbca447858e00000000a9eefb8c49a77795e353cef1a18ddf9f75556b599049cc5c326bbedbccc3cc5aac9e4a4dffff001d0559b918
+0100000029bd58b2759e2398dafa0ed714764e7ea87a61e9966b4777837ad0520000000073173503f0ea326597487757ead4a2afaded9c36545e6f94578d3dc6e5d58fbdef9f4a4dffff001d01bb0d51
+010000008d7f3d0a93bb78055f440276924456111c24af5ed639ca3b3a2226d600000000fce8d0f71b20ad1d4797818e77da4739eabd906dafb8baa38bed17cc3f8d723906a04a4dffff001d04416634
+01000000e26e977c4cabd28830a8e1f87983e91ecbac81030dbbd7cea6efd4c40000000029045a4c17ed6ba6bfc40396d8b9d2640a705fd487c3c0271eafa3c503d0f54b9ba04a4dffff001d02477ab9
+01000000f3249f94c4a61be9fe426f496ddc9670874206c5034c68fb03a77edb000000008f1c9da6092af60d73b296ebbc796caf1b8a095f7ed33078bae45cbd2925029961a14a4dffff001d0253c63b
+010000006f6cd48debac250a7b42738755888a7571eb9935bae9e94d7cc28b1100000000d42144297dd9954a62226db7fa2de24cba50b4c963ae6226ae5fa652946dd0a7e5a24a4dffff001d003cb819
+010000009ff0cf741383d94f79a6efeff2a317aaf6375ac3d252db55362280e00000000029e2f50ac5d203e020e7b59855380dda3e918c80627117c6249f987b28ac807bd6a24a4dffff001d030ba92c
+01000000f61d5499f92cf040f17a16d02b348dcb3dd041b224625bc99acd35a400000000400de7fdd4b27e6d8d358cbe3b329417d75352a9aaff9af70c1f685cf842560675a34a4dffff001d0271c0e5
+010000000d1e2a2e2670286100f862dbae397fef5d8dede11bbcb013c453e8ec0000000043db438150141c0240a7f3f03cca32f4fd0ebbd6bae6daf898a6c7aac99b8a85c9a34a4dffff001d010d4882
+01000000abcc3483c5e1adeec60646454065ae3a116dc60aea65be6d0e66bc7c000000006c0e3cda9dd0367de3a4e1d356c0ad23cd88f26cdff41b412b0ca1de1d9c738cbda34a4dffff001d00b44c37
+01000000e0d79f654270f553820b56d0332b5ec18a4cb5d969c5d67c8adf9b2a00000000f2cf8413e5df690ebfdc8441586639e39ee7d8571d88e07853117ed693bc0b8d35a44a4dffff001d017fc225
+01000000ee4c6ec5ebb31e14991634916e68379e6f08617bd3e2b0816c0605fc00000000203f6062b808d073f0471cc4bad3fe9cc51cb9c732ab53e46a4127fbf0b960fbbfa34a4dffff001d03592dc6
+010000008000bccda2bdbf0a6293178dd1cdce1a368824e9c99587798b7fc1c600000000d29fc4754ff52a23715d3147c6a0ae31ddf06fa70465f4d09d41b4aa08da707bc8a34a4dffff001d05e36843
+01000000386cd6cb2551941a87716c2c618e1e56e9fe7acb49b0a71f414047eb000000007bd7a97b969168050de5f20cd3d0ea212b138f4b43e3c27755f46aa08f98f1ce06a44a4dffff001d019dfd3f
+0100000053402e2e9f85db9774e0193414436cfbbc70b658c4e54913c67bcad300000000fbcf82e5b9b5c1aff879c25c172d4db531905915b205187bc3ae1425603e7c764da54a4dffff001d0154db1a
+01000000ded3e38a879dcbfde2c9ff5690db8377d0c596e20b8be94dafe611df000000009d8b84155b519e0c1cd97837329fc3505f76944245c1bd686617779dc700c15850a54a4dffff001d00ea1441
+0100000011a98d2176e7952434447a06a9ce9f726651737d3a3e006373183b2400000000a50e90ca36c2cab93fd8acec6eae0755d7d2e710688a26fc16557bb5c038d12c0ca54a4dffff001d044b4279
+010000008cffb590fc8e390cc07b0fc7d7e1f83033a7f09c20ef114f6efd73fc0000000098f65f5151c0fdce2398474d0987d7a531aab1c2659675c07658b07cd954f80b29a64a4dffff001d03e15e65
+010000000a7213bcde17cdc86191ebe8580e3a9e893e3b9a189e2787394c5cac00000000230d4c63ebf11994b50ef9c5f1b75b2b8ce6950f63fad01665fec3ed5ff2bcd243a64a4dffff001d02706123
+010000002c29a0a93ef692c20f7a5d0a917a69be60ec5bd73788f9ae92a2e7f90000000048634496267a248713364570b8c4cc6ad5b1a3f32fb74cc1f11fd22c3b293e88b5a64a4dffff001d010845af
+01000000fd50cc2daafbf969480d2675a4cdc41f397d1d815a2aeaf863236bbc0000000086d723685d916a3b3aec61efe9b88a845d96fb903e78760e0759aa2089de4e2e66a74a4dffff001d0389da36
+01000000e897be2520f0c6faad2d0c4be209e0d4cb2edfacb7579a6aa939a10a00000000ae3f02363ed042360b679fece5726b2499e74f27f1bc596c3beaf701463be4465aa84a4dffff001d04ea7f5c
+01000000a4f61773f2105205cff43c8ccb188a0ebe56f0811834cc0772a5a1e8000000003b776a5a9f039715342ed278feea0e87f1454ffdd086ec1e3663cc738965e9d536a94a4dffff001d04479ccf
+01000000cd182da4f3e6641784b8035ac96d74b182939696dda02368f01889b7000000002fa23a7882a373528b5403d34eeb13a26fbf25d668e7019e7a0403ccb5ded71387a84a4dffff001d033151e9
+01000000913cb00e5db68a01cd2c2c469773d0946e3e1589cd72c56349d0405c00000000e04420559afddb03dcf51c67a8317370080e517e24ea38dbbb5bd529680c1d2c3aa94a4dffff001d01ae2ae9
+01000000f87f4f7f7f99202d77747619865391ed07d87afb9c7c9a48b7f319ab00000000b4e21e67bcdbd54bee76d62b5cfdb6b5cdfc5fd1c6ffe9fd6b1e2f39677aeacd2ea94a4dffff001d03275902
+01000000c7a1220a895e2a83917ab688040dea8a000bbd5f4858e8bda00f3bd100000000fcddd0541ddd056f19fcfac0e637cc8fab7503352757707daf37da8e859434f397a94a4dffff001d02d171f6
+01000000c5711e518c4a53e9a6d684759ecd07d69b5d859e1edffcf4bcd3896800000000e4ce2882f8c5b8e467a080c647ca97b8c11070b7ad10a1eaeef13e1b49d0028cdaaa4a4dffff001d03ac5ea8
+010000000a1bf9a1db633b5c817be9ecef65ea99ed980a58d070cfab68b70d5200000000c9fca844eac0515369a587359fce6a2cec83ebe565dbf46b92928ca831a1abd54ab24a4dffff001d0370f65f
+0100000081f9fcc96f7824b23225d5526b65d3d5826bfa8b65c083028e7b287700000000163cdfb6b5d8589c539ef411a96eb96ee49dcf03ef4538d8f752762893db209515b34a4dffff001d0215c77e
+01000000a563fe8e23fd9acdbd8ce0db616cb72c1b05fc8610c4e89fcd32b21f0000000074fccc385cc517580daf8966fcbae398b81ce1f077bdc5732c529d6c3502e16183b34a4dffff001d033652b3
+01000000de5ca94254b771527679b5dc4862a7eee044409cc33ab2960cbe3920000000005f20491b7a3093fe2b342e816a5b8d409916c2f11a6cb6743dc528e8c7ac51b7ccb44a4dffff001d00d437a6
+01000000f8d57b05a64cb39a5adff645ba434bb707aa118322851cbce137df2800000000d760a34f6e7b07b3718a8355b71b014f07fadb917707e4bb635cc1277cb8ca7f5ab54a4dffff001d00d8ddeb
+01000000a54d55adebdea113e46d6911a15712f7545709577a5ef6684a37974800000000849ecac186963e52f8b855e51f70e9cfc90cd0f7054c23f1bcbc1675206a00bb4db54a4dffff001d01cec36c
+01000000f2f0b39c9e0f8ae81ceaf09ad25ee9166e8f3d33addaca1ca1366c5100000000060e3cb266839d6c8155ebef78939c33b659156bb2cbca1cc95b50e58ad5100b2eb74a4dffff001d02e282eb
+010000004a016d8b02a6e3c33705715c29754230218f04485693994b29d78a2c000000007286500c919f491aa5a0721558e4b8a64230ec2d1d955c45ac871da0e74c898d3eb74a4dffff001d036714f3
+010000002d24b64aca8efd3a0a948e813c5303891d1971e6295be97c1923e05c00000000eca7286e07ea252409b51cb2124e3fcd6adb8e6fdcf87046de18666e36037511e6b74a4dffff001d037e372e
+01000000dc8280b9a38ed7829f3ae49bf8f01642c0b16b90642b2e911c2cb69000000000a10c2661ab1b02ab9f3c5bf928db087c27c06ee7537b04ee66ea4785fb625c6205b84a4dffff001d053fb75f
+01000000280fd86185912d306966b6b7bd90a765e659bad17564825f0c997f2d000000006637b339af8cdf579a250267ce7ce41f19de798c7ee6ff7823312076e0950962c7b84a4dffff001d00c1fc82
+01000000993b4428fc00d1eef46e5fb166b32f32ea75e3e49105a04322845d2800000000fbdd76c6562c86af0e63ea9a6c46a91b8d92c4e7df480977ec0d53b7f3c25b9812b94a4dffff001d0099e992
+01000000c3b9d6eb714b91e367d7a44145f498cdde7f579b68c66bc1b9af7886000000005b0df3f16d5bb399b09989beaf8e0665c695ef49b7aa8473ba8690f0b029ce0094b94a4dffff001d036efa36
+01000000a702519acf76d7519ce42d6c0bfff12644be3694621a942a55e4b36900000000f9357358ee5931b907a8c3df01115891a740ce6f4df224eda4586eaf450d8b9f3bb94a4dffff001d02d9d39c
+01000000931d5230c5a2b7d43d322cebf9026ca68fe1a70afe0d8c8abed78d320000000005634a2446d5fb785d1f1582e6f35418bc6fbfb6abb9507213b010e58dac1c3ce0b94a4dffff001d02e9d793
+010000009c7bf22857a46add066ce2267450ddd5a6f280943e961b1db4d3be0a000000005eef5906ff42a8baf4a8a2d8119f88112d802484361ea778d22e44300f6ab20c55bb4a4dffff001d0179f4e9
+01000000e2a926d9239573962e534a47311e7875482832ecc6306bfaf1137c0500000000fe6ae11390e7bca224e9937759c658c9aadc8971e8ba384caf2a58de311826e7abbb4a4dffff001d01d2102e
+01000000dd3fb5ca9283091e24a2dec99871d9c033f39b1009f5ac592ae2cf190000000086a971609dbe58f45f99ae1a769c07ed6b0575ffe811844b8f6e04edac722bdae9bc4a4dffff001d037251ae
+01000000a7f31bcf8d5a86bda0ddaadc3c8b77ec71af748584b3a5eda6b88c4600000000adc6c0ba4cebd8e2b0154e9f876f1e6ea54e293e6505a5200a4a75d394521a7882bd4a4dffff001d02defce7
+010000003846b6ba73428b23c1aea96cb3a4cf8b72ea4f40268ed68fd16ad9bc0000000050a4b5b4d3fb6c72d198506c83d6e2792262134ac07813baf355921ffee7c6579abd4a4dffff001d04687d8c
+01000000df7e6b1a5947867dda62ed7a79f75d939d4190f3a575b447c31308f9000000002523ea33f4e43dc2dbdb14116d5d087aa3544a7f886a69693caa154ae0abcfdf05be4a4dffff001d030a6d3c
+01000000799f7e9b6d0ef1bcc5d4e6b82936272e3492df8a920b934dc9e1973000000000d0038b40a4b3c20cb7a6b431d6c2edcd235c59dfaca4698dc0f4a057d89eded6ebbd4a4dffff001d01e9924b
+010000005dc85295d33956fc24736fe54dfaa12ca98cae2ba02ef7eb9655a12a00000000b7bae6ad3a263adfc01e5e76acfa7dbdc0e60962230af6fc59003d59b437b28d90be4a4dffff001d032ce3c6
+01000000a3b52bc4a8ade1fb7764462099a412272fa13b3d9de508c356a3d7910000000001eef5a3b0e24e946235bdbdaf78601185042a5974a6f9aa7d8deab62b6bc49c18bf4a4dffff001d05a9f6dc
+010000000e816fbf3db7c990098f50722e36029dda668e9aa386c23a201e9d8e0000000027d9b4ef4291c5332a05c4a2918b5526b96a117c0bb2bc97cd18f5a6eea15caca7bf4a4dffff001d023f105f
+01000000514062fccf6e88a8ad9d0ff0fa2ca4d2716c2b5fcc577f52e4557fab00000000ca12c4c5fa9b8106d21f12f4bb24e1954ee95686b849c643e0504328653198fe1cc04a4dffff001d022e817e
+01000000b8631eb7fbe8abdc32f81b54c6f86c96cdb5d7c270cbc8e8026890e80000000032510d84003ef4d4b634cb4bc914de16f8e9b72b6f1ea20dd9eb847f11351641f7bf4a4dffff001d011d83b5
+01000000bf3ce738c0692a474dadc784640fe4e2f06beef2fffed0bfee65cff100000000a463353790f4669516f33c405164df2ee08f7738bc91ded3ca2a025e18143ac967c04a4dffff001d01fcc651
+01000000fd4bb907a2089b41433dde0fcfa70db3ac3a6f075be657f0e15144d1000000004bc439544765cbaef9a184ce7fb26bd62cf1ab32267eed5fb3bc7050a8f0c427b4c04a4dffff001d05d07be8
+01000000d0d3565bbad3a5fd165a7fbd978fb2ff2974dac1fb4ef21ee0c6282200000000e4e9ef647dfd719c086990944594c3892d0deffc18fc90e092d61a3e586e4cf8c6c14a4dffff001d0188a8b7
+01000000d2d5324fd546db69e8c42e6f6c5f2f686cf29a9fae985224d450ca7b00000000b3c4d587370073598ce1a226be2f70e0ccbe446e80f88f0446dc5b179782186b78c34a4dffff001d027bf975
+0100000074b16276f3e902ba74a5a938a86ad7249db7c620b06023385b515109000000001b94a65422bddf5384bb8da9d866fc7a7e5c5abfbd5867efdc8eada3b78b8e3babc34a4dffff001d02d402ae
+0100000057f6b87254267089a1d84eeb06f20c3b181177372b88cb9152f356dd00000000c70258c4f96177b1cdd9d6fd5ad0e9a65abb44e7fc3dbe507f45e2984ab830d9c9c34a4dffff001d03cfee0e
+0100000078eea9a9282653e1e213c716004ee3c5b7d323c37bdec7a64565831200000000ac37dfa5948ecb63e944a61eb0e83cbf7dff61cd61cf66d21f447d9a97bcbec123c44a4dffff001d05c7a466
+010000006c0d72985fd57581d55754a3dc631a89e6e4e1edbad1696ec271d6c200000000c1d26e829e285126d03a1943bc46d72c7e68f7fe1855393079dcc6406328ebc53ac44a4dffff001d01ec1ff0
+0100000060f628c70ffabdde8c265ff6aa1b49e0f42d250a0645ac0fa2963ffc0000000074f7d49e03dd5ccae2109db1e4ff11507238642d86fc771aaa999ae48b06f3d26fc44a4dffff001d044b6b47
+01000000f2c8d522c94e93a8ff5d947c057e4125a4deb167e5eabed5dfb56736000000005127f86da79df2980cedb16ab9a4b167e6daeadcda0d645f83deb48a3dcc065eb8c44a4dffff001d0561c0ee
+01000000e6c388d72c717914e507f11c58a139137fd483dbd746e9c5f2a1eb0f00000000afa8dd6f0e319d4ddedfa1425f6c03e461dfa005d42d3f355a04cae42626c9b121c64a4dffff001d059f05b2
+010000001b354bd6d10f5a24879851bb2aff42548cb66cebda04e7103bc2521900000000622c689b318941bef087640fddab2c5ef6dd6024f133ac28c1ccfaf30e380bfa4dc64a4dffff001d00484506
+010000003e226e00e126f0a4a35b1c6c09509e0d999122428262a5303e563c6000000000d7432b21fd48689d7e79f9b6f7f624aefe8b72503fca13b814d355618b9023cf23c74a4dffff001d0247f874
+01000000fbb3060018eb1f40d734cc64744134d96536c4a331e00d4c05e2cf9b0000000061b34d561ddfd18ea3381d48a8bbe4627727c6ead00a84030e7f76eeb788148a71c74a4dffff001d012e783d
+01000000fff008ec46c2d35cf469eded16dfadc77ca6e9a9680cea0b0611661b000000001d1e1087d20d41da1f0b4a5423aeb281ecd48cfc434feb45a4c6c31b6b61398124c84a4dffff001d05928767
+0100000093ebaafdaac804feef3ec352e40cc78be60e7ef1edb167891ccd99cc000000006bf225e961b1f1b8e042c45966c2118f20871551e0996eaf51bb628cd9d49b7a45c84a4dffff001d00381daf
+01000000e85fb976eb4817418c0ed2987dba1674c0fd757f70b470f83d01a2d300000000fd6717e5d49bf303d41d861fc50503aa8575eb52243f426de308869a8cd57c57f5c74a4dffff001d033a590f
+010000009cf5f976b9ae634b4c867bf78cb602bee8150ea1838d0ef3d06ce94700000000fe6ce75c462d7d09aa8d917fa90a49bd6a4c41f02457ff40ca47ded089021042b0c84a4dffff001d03a5f6aa
+010000002eff16b6669f88de6f40810c57349804014f734c692a44ee822b7f1200000000c095ce4ab7e0e02110b75a33012da97d75e26b83de5ae0bd392fc3b8191b77e8e8c84a4dffff001d032c2bf6
+0100000001aa2c494f2d4b7ecf367734c734b931d3592b1572dcff557186ece700000000a10e60b120c06cd1be1d2153abb7901e2d9f1a3f43de9e6642ad269be1cae22e44c94a4dffff001d01f2005b
+010000003d04e058642d22428704ad4337f372bb8574cae072c2a4b469b2e1dd00000000bff07e723c3859fe85bb849d860b21a325b1baf1494a3c1e73435dd7992bf8e53dc94a4dffff001d00262757
+01000000272ecd270665dc39e924838516da62f8588270f1e37812aabdb1d48c000000001725d4769aaca3cf86c5b0dd199bf93f2d16dbb13fbd4029633bdd1085283fdd13c94a4dffff001d02f79a8a
+01000000705f1bbf68b4976a9ade4ff01ae933030cfe4cd43e8092c9604442a2000000000cb7f30461a3563992f7b02095263fdceb13984123a28ea68d0989601655e84ab8c94a4dffff001d0017e0d9
+0100000082d6e6bf4ed6c5b989705920effd072fe6b7119ea5b8c8fa6d0bf5c70000000071959f90386cdc263655d36033817a6f69cbc2d99fc56cc81f893b9787907a4fcfc94a4dffff001d000f9093
+01000000374e3a88a3a3eef84b4212e90e863754ac2a6999747c22667c0b29e0000000003a0e3693a8455ac94beb574efd98297948c5b5f69eaf0b813e6d5e57426e39e279ca4a4dffff001d042b079c
+0100000003f06b3017023d1cbddd4cf169c4e74879754683f0d7670f1d1197f70000000098b12eacd55084fe2807a33dc2c89e6fc25f382aba3c6b4c7ee2f74a6999a6bbfbca4a4dffff001d03ba1eb8
+010000009c0730eadadb66fa486e40dbddd14b635ec7939f788fdfd999e043f2000000006b0a86109aea3d5e4b7dc320da600dff19d81dce7b77dab9596204c20c6125b06eca4a4dffff001d01c88a1c
+01000000410f77945e68dba04d76fc931b26ccb571159c9bad438df5f8fc4d190000000004d902fedee34fce75de583a54e1fe7ef26a7946b48b93b9b0834dbf19a0b47f3dcb4a4dffff001d016618bc
+0100000025ec91d09a451f18667262004e0cebb2ad66262407ed0a8f4ddbff580000000092f8e1be93272aae6b36766d721df3d80444582edec0143174c5fafad4cef50c79cd4a4dffff001d031ee0cf
+01000000d8d0cea1249f726362ee78259d97d98efa17ab0efeb1b9d0a0cf0d880000000035c3dc39e544b855457fb0711697a24123fc3ba6c439123714b193b9d1d9ec7514cd4a4dffff001d0526c906
+010000003e824b7a8cb7bea1dddcaee7bbc4720e8c5d2f53c9c28dbaea4662a90000000061ef7a1ca4f6f61cc594d040490d3cb2f11054639a0a964c67cebc6dbc079e36cacc4a4dffff001d03af40bf
+01000000da9201093da940fb6badc420406540a979cd5c06fc2f47db3215130b0000000069577de9d33e6351fb0ac1be26158f94c217d2522c35e9f813e47bf9811fad72dacd4a4dffff001d02f70faa
+010000003dca51f6b97f7c05a44f329c1c9f4d7cf03eeb3b16d94bd56e34b50600000000092122ea597797ea0de7841945f09c221578800d0c44de4961cad7ecfb93c47323ce4a4dffff001d03bd9635
+01000000a560b2ead8a3f50df84385c1d5a51654c22a321268e077dfcb9ef90c0000000091db9cfec3e6c44a790d6295a2effcb8dc94f9f1c0fc4f6d867e90e140c1e9f325cf4a4dffff001d03a86875
+0100000056e6a9a6b93f9ad3ad847ee845ee9c366dc9376489516c31d892ed5e00000000bf2f3b6867855fe1058092fec7c5ea89c15cf8a70d0c565c26362d10129221af47cf4a4dffff001d027a9b69
+01000000d123e40889dc9dd8039943b5b1a0de53026e9ce92cfc69d5f8482fbc0000000022b49f0319707490d002d5b2c04adfc7a6433c2931ed04545e9c33f37a12b5bb924bbe4fffff001d00b7fffa
+0100000065aa216d118366b22580747fb4d876d80ae3be145e318563984e0b040000000000c2b33efaf934623c6bea64570558d6551524e2f45d022b0fe935387279e6a37b51be4fffff001d0511e593
+01000000eced0b25fadf8818fabbf65b9c16f39874336d7faca51b74698ad55500000000ee49643ded3202eb46b451c55a652c6cf38d82da93de97a2f2bc0e8a28c1be7ba951be4fffff001d01f82bc0
+01000000bc9c266783225bfa273dbba2342f54809dcc2b263e1e6bf3269bda300000000001aa244355f8827bdc7eaaa0defe1a928816cf7d919890781c1fe3ee58d985684d53be4fffff001d01a443c5
+010000001b7467dcebcf2baa4814161dd6c7ab966ba082d670a69eaacc1785910000000050098a241f8df957a6bbea8ecc39473091636a4e55d673ae723edb6a3f50b9835853be4fffff001d013788f6
+01000000f5023ad6b3432b6b2d5afe707cfbfa0a30e94000799c8430a97da151000000007a53e7dbfb43a28ee9d5956be8da4190e5be6f16da4a5636f6139742754ec0cedc54be4fffff001d04474930
+010000002077ea8e53ba9a132d83b91e40fb1f4c724217b8197c4533a5bee9e90000000096a376627bd7b42278fb33609713b83665f195d100316b9e1651638cd4d7d47cfd84bf4fffff001d027c40c5
+010000001936a4ef861f194c15f171e843866f33ec3b3cf09394bc323753beb8000000002e3288941ec156d0fdc90d314d85a71a418d272704e12f2a99576fe3201e2d1b3887bf4fffff001d027aefaf
+010000006e5c2e805629d476b92e0a8d74c3ee0dea0c24e9f35ab4afc6ac77b400000000d26cbed9a7c1e51fe657c6b3cdf202926e0b6e371ebf41ccdcbc0909470d56256e88bf4fffff001d05a2db35
+01000000e5f26b5abf56353520e1f9baa35e9cdeb57f257f59ff6a4ee42c3795000000007becb215d446a70b351fdb14659d105b3c258802fa4314b536c4e5249adaab2a9c88bf4fffff001d04e36b51
+010000003cf3550994f31a27c040b1340c7ae9ccd36d554a1bfa797cf46504c00000000005f56011e8d4bf55bca3dd8c9799e0d83304e43ae8b66ee58c688bb9d7f7fefeb088bf4fffff001d0314c7c0
+01000000ad48a4d4922e3834375f8c50e8cb1842688829c8952204f66b89a7e9000000004579dc3ff13abb920007843862e9091b0e142564cd0a75f5fc3272982b816d4ee988bf4fffff001d00becf18
+0100000098eff668afac04d3d8531d9bc31aafcd48f7ba68b080b40641dfddc200000000a91184d97bec523016c15dfc3473e7407278b70638f4fd6c9dc1ba210454f7fb1189bf4fffff001d042a1349
+01000000ed2b2b04575642de5fc665cb8e30f3e39dea66299592405204bf6b5d00000000f3af0d684b2c16f307b6ddf9a02b58b9079e858dd2d093aa29288856b83b82183789bf4fffff001d002cfc38
+01000000a398a8e978de6f1e7b9b84523d45e5f2021feb29f6d39890a2f48afd00000000f4a78ab4703ca95ed478e78e526f1a8198c3cbfb8a9ee6455338d517aa18173f3e89bf4fffff001d008b40b4
+0100000036bef7eedabe3b05e1e049efcf66823190285d64d11ccfd2eccb5e3200000000243f2ed308bd48cd3dd25d4cc649dea0d0518d6e12612e03c8a0888aa284bf2d6189bf4fffff001d0498f47c
+010000008d84b4fadc45563b60c0f50797ffaf71046f287482ab9fe629f64e4e000000003ae5c2515afb4117d032de958e0ba535e484575b80ff6d6e2e0a23e6dc95e0ea7889bf4fffff001d0518a2da
+0100000020ddc91a15f8c9ec5161bf1709ad7f37c33ecce9809b353dfd3df2a400000000f2b6d372af2c7164d36dc1c369090f4f9179b0870676ab6ec2641b0922b1fd179289bf4fffff001d04bbff73
+010000009701e67638ec4e8b47c547ac3125ac6c1892a98d77a580ab88f069d5000000007c740055656afd61883f9c1c102fec710315e6e4d58904accd184cba85c9f958cd8bbf4fffff001d03f8f01b
+01000000fa9f56e3e4fe1352905b3a1f12567caa3f9f4251f0bda94fb07f1684000000009ff7fbae47f6afb47a1723662d196155b5f4bc1c62e882a93f54ba2d9a10913b508cbf4fffff001d02d1c97b
+0100000002ae1c66166259ef43d290c6a60cf8eb5783e6c91293a3566946673a00000000846d597237ea76576bc49381badb59bd509cfda3095c2c1ce5c75788b96aa144718cbf4fffff001d0139c1f5
+010000001ed0a06499985c4cfb05371eec681819d8e193b66011f57b66792565000000008980b714aeb09568fd34128f4c7ff84d3e09223dcb2b290c5948816cc636d69fa58cbf4fffff001d0122c5f4
+010000008c045f9e0e200a8eea9c0957b12e045e927dc768a8a5fc2d8fd8d9ad000000009925a3f617c025b95f6329247379b7ef7bd081e52838676671c7360eec07255a098dbf4fffff001d0099c40f
+01000000a6adbd81b12cff9948911af583db14484c4c0d30e91b1af2e7fd4d4f00000000a70e4b2057854c71170f8468b799f03d0344905213872216f4210f7bfcf76980198dbf4fffff001d002b7c3a
+0100000059e1e5ae6727e02c2acf95bc5f24d8274e42495c790125ba52a581170000000043412a5a9f1749688460eee46b62851641fd9b17f9ed5131b3f50e4f160eadc45cb3bf4fffff001d03a966ba
+01000000c704e90a0c4cb992d6973d636a7094ef2790a60231db345a926b6cb6000000006f98c89001a2e89ea35aaf00b85d965af5598b61bc48d9d9847609331995383effb4bf4fffff001d02ee5af9
+010000005468467a64f255415f3ad770f0d5393275f2813fd0e1e915a2b8c28b000000000566f29ed6b8cdf85a4b197dcbb0135dfb7b671f83ca67970bffea903309ab7445b5bf4fffff001d032ac068
+0100000021ad75b801d48a30fa5b4e6b1e43106a294a5e2763307a992fbd5282000000002c20851e58dc1a2ceb7c7771822ed41c7d0994731d01829628c3e938f4155a276eb5bf4fffff001d01a1420f
+0100000002954bf32d68d54adac42cb5190efc06f18ead35208dc14a8face6f1000000009002c5c2dfd0259960507e69399c688f0d74bbcd52c810cd2d2507a829133f4941bdbf4fffff001d01724be9
+01000000add5df18f427437ace8b40064b3806583217a3f724672cf72cf8c18300000000bc652800c84513b2de38e0e97be92a582a628e1e49a9fe8d49aea5623251eda54abdbf4fffff001d045ec8ea
+010000003b0c953fed585c1ac952df84e4c3953ba551da457c700035f6f3fc0c00000000831c01723b0fa955a6fbd51d9ddc5c77964814868f96f52ea3eaf3b66098e6df9dbdbf4fffff001d00cb77db
+01000000f6667e8c4557c28f88b86a6892d03509a5afc9a89088a5bb8638eafa00000000ff61d87fbc86dac7f80962702153a2a6d9a84dac13c265106b883ed7ef68fc91cebdbf4fffff001d00e07f63
+010000002cb99b76cd981a422d02d61684f76bacea92669298e552e412ce41df000000005f2a9977b14cf34078500f956be0cc154291ce5a0e34c00a3dbbae97bed5e930f5bdbf4fffff001d008fd760
diff --git a/test/functional/data/wallets/high_minversion/.walletlock b/test/functional/data/wallets/high_minversion/.walletlock
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/.walletlock
diff --git a/test/functional/data/wallets/high_minversion/GENERATE.md b/test/functional/data/wallets/high_minversion/GENERATE.md
new file mode 100644
index 0000000000..e55c4557ca
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/GENERATE.md
@@ -0,0 +1,8 @@
+The wallet has been created by starting Bitcoin Core with the options
+`-regtest -datadir=/tmp -nowallet -walletdir=$(pwd)/test/functional/data/wallets/`.
+
+In the source code, `WalletFeature::FEATURE_LATEST` has been modified to be large, so that the minversion is too high
+for a current build of the wallet.
+
+The wallet has then been created with the RPC `createwallet high_minversion true true`, so that a blank wallet with
+private keys disabled is created.
diff --git a/test/functional/data/wallets/high_minversion/db.log b/test/functional/data/wallets/high_minversion/db.log
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/db.log
diff --git a/test/functional/data/wallets/high_minversion/wallet.dat b/test/functional/data/wallets/high_minversion/wallet.dat
new file mode 100644
index 0000000000..99ab809263
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/wallet.dat
Binary files differ
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index a2726763d0..70dfe81d4e 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -186,12 +186,15 @@ class ExampleTest(BitcoinTestFramework):
self.log.info("Connect node2 and node1")
connect_nodes(self.nodes[1], 2)
+ self.log.info("Wait for node2 to receive all the blocks from node1")
+ self.sync_all()
+
self.log.info("Add P2P connection to node2")
self.nodes[0].disconnect_p2ps()
self.nodes[2].add_p2p_connection(BaseNode())
- self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us")
+ self.log.info("Test that node2 propagates all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py
new file mode 100755
index 0000000000..62c3eca07d
--- /dev/null
+++ b/test/functional/feature_abortnode.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 bitcoind aborts if can't disconnect a block.
+
+- Start a single node and generate 3 blocks.
+- Delete the undo data.
+- Mine a fork that requires disconnecting the tip.
+- Verify that bitcoind AbortNode's.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import wait_until, get_datadir_path, connect_nodes
+import os
+
+class AbortNodeTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def setup_network(self):
+ self.setup_nodes()
+ # We'll connect the nodes later
+
+ def run_test(self):
+ self.nodes[0].generate(3)
+ datadir = get_datadir_path(self.options.tmpdir, 0)
+
+ # Deleting the undo file will result in reorg failure
+ os.unlink(os.path.join(datadir, 'regtest', 'blocks', 'rev00000.dat'))
+
+ # Connecting to a node with a more work chain will trigger a reorg
+ # attempt.
+ self.nodes[1].generate(3)
+ with self.nodes[0].assert_debug_log(["Failed to disconnect block"]):
+ connect_nodes(self.nodes[0], 1)
+ self.nodes[1].generate(1)
+
+ # Check that node0 aborted
+ self.log.info("Waiting for crash")
+ wait_until(lambda: self.nodes[0].is_node_stopped(), timeout=60)
+ self.log.info("Node crashed - now verifying restart fails")
+ self.nodes[0].assert_start_raises_init_error()
+
+if __name__ == '__main__':
+ AbortNodeTest().main()
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index b7814bf33e..1b434c4485 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -40,7 +40,7 @@ from test_framework.messages import (
CTxIn,
CTxOut,
msg_block,
- msg_headers
+ msg_headers,
)
from test_framework.mininode import P2PInterface
from test_framework.script import (CScript, OP_TRUE)
@@ -72,7 +72,7 @@ class AssumeValidTest(BitcoinTestFramework):
break
try:
p2p_conn.send_message(msg_block(self.blocks[i]))
- except IOError as e:
+ except IOError:
assert not p2p_conn.is_connected
break
@@ -180,7 +180,7 @@ class AssumeValidTest(BitcoinTestFramework):
for i in range(2202):
p2p1.send_message(msg_block(self.blocks[i]))
# Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
- p2p1.sync_with_ping(200)
+ p2p1.sync_with_ping(960)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index fdb60fb0e8..fe6f9eade1 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -14,8 +14,8 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- get_bip9_status,
satoshi_round,
+ softfork_active,
)
SEQUENCE_LOCKTIME_DISABLE_FLAG = (1<<31)
@@ -29,7 +29,10 @@ NOT_FINAL_ERROR = "non-BIP68-final (code 64)"
class BIP68Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.extra_args = [[], ["-acceptnonstdtxn=0"]]
+ self.extra_args = [
+ ["-acceptnonstdtxn=1"],
+ ["-acceptnonstdtxn=0"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -49,7 +52,7 @@ class BIP68Test(BitcoinTestFramework):
self.log.info("Running test sequence-lock-unconfirmed-inputs")
self.test_sequence_lock_unconfirmed_inputs()
- self.log.info("Running test BIP68 not consensus before versionbits activation")
+ self.log.info("Running test BIP68 not consensus before activation")
self.test_bip68_not_consensus()
self.log.info("Activating BIP68 (and 112/113)")
@@ -333,12 +336,12 @@ class BIP68Test(BitcoinTestFramework):
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
self.nodes[0].generate(10)
- # Make sure that BIP68 isn't being used to validate blocks, prior to
- # versionbits activation. If more blocks are mined prior to this test
+ # Make sure that BIP68 isn't being used to validate blocks prior to
+ # activation height. If more blocks are mined prior to this test
# being run, then it's possible the test has activated the soft fork, and
# this test should be moved to run earlier, or deleted.
def test_bip68_not_consensus(self):
- assert get_bip9_status(self.nodes[0], 'csv')['status'] != 'active'
+ assert not softfork_active(self.nodes[0], 'csv')
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
@@ -378,7 +381,7 @@ class BIP68Test(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
- self.nodes[0].submitblock(block.serialize(True).hex())
+ self.nodes[0].submitblock(block.serialize().hex())
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def activateCSV(self):
@@ -388,9 +391,9 @@ class BIP68Test(BitcoinTestFramework):
height = self.nodes[0].getblockcount()
assert_greater_than(min_activation_height - height, 2)
self.nodes[0].generate(min_activation_height - height - 2)
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in")
+ assert not softfork_active(self.nodes[0], 'csv')
self.nodes[0].generate(1)
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active")
+ assert softfork_active(self.nodes[0], 'csv')
self.sync_blocks()
# Use self.nodes[1] to test that version 2 transactions are standard.
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index ce353b2272..c74270febc 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -74,11 +74,15 @@ class CBrokenBlock(CBlock):
def normal_serialize(self):
return super().serialize()
+
+DUPLICATE_COINBASE_SCRIPT_SIG = b'\x01\x78' # Valid for block at height 120
+
+
class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [[]]
+ self.extra_args = [['-acceptnonstdtxn=1']] # This is a consensus block test, we don't care about tx policy
def run_test(self):
node = self.nodes[0] # convenience reference to the node
@@ -96,6 +100,13 @@ class FullBlockTest(BitcoinTestFramework):
self.spendable_outputs = []
# Create a new block
+ b_dup_cb = self.next_block('dup_cb')
+ b_dup_cb.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
+ b_dup_cb.vtx[0].rehash()
+ duplicate_tx = b_dup_cb.vtx[0]
+ b_dup_cb = self.update_block('dup_cb', [])
+ self.send_blocks([b_dup_cb])
+
b0 = self.next_block(0)
self.save_spendable_output()
self.send_blocks([b0])
@@ -486,6 +497,14 @@ class FullBlockTest(BitcoinTestFramework):
tx_last = tx_new
b39_outputs += 1
+ # The accounting in the loop above can be off, because it misses the
+ # compact size encoding of the number of transactions in the block.
+ # Make sure we didn't accidentally make too big a block. Note that the
+ # size of the block has non-determinism due to the ECDSA signature in
+ # the first transaction.
+ while (len(b39.serialize()) >= MAX_BLOCK_BASE_SIZE):
+ del b39.vtx[-1]
+
b39 = self.update_block(39, [])
self.send_blocks([b39], True)
self.save_spendable_output()
@@ -750,7 +769,7 @@ class FullBlockTest(BitcoinTestFramework):
# Test a few invalid tx types
#
- # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
+ # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
# \-> ??? (17)
#
@@ -776,14 +795,14 @@ class FullBlockTest(BitcoinTestFramework):
# reset to good chain
self.move_tip(57)
- b60 = self.next_block(60, spend=out[17])
+ b60 = self.next_block(60)
self.send_blocks([b60], True)
self.save_spendable_output()
- # Test BIP30
+ # Test BIP30 (reject duplicate)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b61 (18)
+ # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
+ # \-> b61 ()
#
# Blocks are not allowed to contain a transaction whose id matches that of an earlier,
# not-fully-spent transaction in the same chain. To test, make identical coinbases;
@@ -791,20 +810,44 @@ class FullBlockTest(BitcoinTestFramework):
#
self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
self.move_tip(60)
- b61 = self.next_block(61, spend=out[18])
- b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases
+ b61 = self.next_block(61)
+ b61.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
b61.vtx[0].rehash()
b61 = self.update_block(61, [])
- assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
+ assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize())
self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)
+ # Test BIP30 (allow duplicate if spent)
+ #
+ # -> b57 (16) -> b60 ()
+ # \-> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ #
+ self.move_tip(57)
+ b_spend_dup_cb = self.next_block('spend_dup_cb')
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(duplicate_tx.sha256, 0)))
+ tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
+ self.sign_tx(tx, duplicate_tx)
+ tx.rehash()
+ b_spend_dup_cb = self.update_block('spend_dup_cb', [tx])
+
+ b_dup_2 = self.next_block('dup_2')
+ b_dup_2.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
+ b_dup_2.vtx[0].rehash()
+ b_dup_2 = self.update_block('dup_2', [])
+ assert_equal(duplicate_tx.serialize(), b_dup_2.vtx[0].serialize())
+ assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.hash, n=0)['confirmations'], 119)
+ self.send_blocks([b_spend_dup_cb, b_dup_2], success=True)
+ # The duplicate has less confirmations
+ assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.hash, n=0)['confirmations'], 1)
+
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b62 (18)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ # \-> b62 (18)
#
self.log.info("Reject a block with a transaction with a nonfinal locktime")
- self.move_tip(60)
+ self.move_tip('dup_2')
b62 = self.next_block(62)
tx = CTransaction()
tx.nLockTime = 0xffffffff # this locktime is non-final
@@ -817,11 +860,11 @@ class FullBlockTest(BitcoinTestFramework):
# Test a non-final coinbase is also rejected
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b63 (-)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ # \-> b63 (-)
#
self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime")
- self.move_tip(60)
+ self.move_tip('dup_2')
b63 = self.next_block(63)
b63.vtx[0].nLockTime = 0xffffffff
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
@@ -837,14 +880,14 @@ class FullBlockTest(BitcoinTestFramework):
# What matters is that the receiving node should not reject the bloated block, and then reject the canonical
# block on the basis that it's the same as an already-rejected block (which would be a consensus failure.)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18)
- # \
- # b64a (18)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () -> b64 (18)
+ # \
+ # b64a (18)
# b64a is a bloated block (non-canonical varint)
# b64 is a good block (same as b64 but w/ canonical varint)
#
self.log.info("Accept a valid block even if a bloated version of the block has previously been sent")
- self.move_tip(60)
+ self.move_tip('dup_2')
regular_block = self.next_block("64a", spend=out[18])
# make it a "broken_block," with non-canonical serialization
@@ -870,7 +913,7 @@ class FullBlockTest(BitcoinTestFramework):
node.disconnect_p2ps()
self.reconnect_p2p()
- self.move_tip(60)
+ self.move_tip('dup_2')
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
@@ -882,7 +925,7 @@ class FullBlockTest(BitcoinTestFramework):
# Spend an output created in the block itself
#
- # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
+ # -> b_dup_2 () -> b64 (18) -> b65 (19)
#
self.log.info("Accept a block with a transaction spending an output created in the same block")
self.move_tip(64)
@@ -895,8 +938,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to spend an output created later in the same block
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
- # \-> b66 (20)
+ # -> b64 (18) -> b65 (19)
+ # \-> b66 (20)
self.log.info("Reject a block with a transaction spending an output created later in the same block")
self.move_tip(65)
b66 = self.next_block(66)
@@ -907,8 +950,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to double-spend a transaction created in a block
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
- # \-> b67 (20)
+ # -> b64 (18) -> b65 (19)
+ # \-> b67 (20)
#
#
self.log.info("Reject a block with a transaction double spending a transaction created in the same block")
@@ -922,8 +965,8 @@ class FullBlockTest(BitcoinTestFramework):
# More tests of block subsidy
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
- # \-> b68 (20)
+ # -> b64 (18) -> b65 (19) -> b69 (20)
+ # \-> b68 (20)
#
# b68 - coinbase with an extra 10 satoshis,
# creates a tx that has 9 satoshis from out[20] go to fees
@@ -949,8 +992,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test spending the outpoint of a non-existent transaction
#
- # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
- # \-> b70 (21)
+ # -> b65 (19) -> b69 (20)
+ # \-> b70 (21)
#
self.log.info("Reject a block containing a transaction spending from a non-existent input")
self.move_tip(69)
@@ -965,8 +1008,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
#
- # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
- # \-> b71 (21)
+ # -> b65 (19) -> b69 (20) -> b72 (21)
+ # \-> b71 (21)
#
# b72 is a good block.
# b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72.
@@ -994,8 +1037,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test some invalid scripts and MAX_BLOCK_SIGOPS
#
- # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
- # \-> b** (22)
+ # -> b69 (20) -> b72 (21)
+ # \-> b** (22)
#
# b73 - tx with excessive sigops that are placed after an excessively large script element.
@@ -1218,7 +1261,7 @@ class FullBlockTest(BitcoinTestFramework):
self.save_spendable_output()
spend = self.get_spendable_output()
- self.send_blocks(blocks, True, timeout=480)
+ self.send_blocks(blocks, True, timeout=960)
chain1_tip = i
# now create alt chain of same length
@@ -1230,14 +1273,14 @@ class FullBlockTest(BitcoinTestFramework):
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1), version=4)
- self.send_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=960)
# ... and re-org back to the first chain
self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1, version=4)
self.send_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2, version=4)
- self.send_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=960)
self.log.info("Reject a block with an invalid block header version")
b_v1 = self.next_block('b_v1', version=1)
diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py
index 3a4889bbe9..6f01f97ea2 100755
--- a/test/functional/feature_blocksdir.py
+++ b/test/functional/feature_blocksdir.py
@@ -18,10 +18,10 @@ class BlocksdirTest(BitcoinTestFramework):
def run_test(self):
self.stop_node(0)
- assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, self.chain, "blocks"))
assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "blocks"))
shutil.rmtree(self.nodes[0].datadir)
- initialize_datadir(self.options.tmpdir, 0)
+ initialize_datadir(self.options.tmpdir, 0, self.chain)
self.log.info("Starting with nonexistent blocksdir ...")
blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir')
self.nodes[0].assert_start_raises_init_error(["-blocksdir=" + blocksdir_path], 'Error: Specified blocks directory "{}" does not exist.'.format(blocksdir_path))
@@ -30,8 +30,8 @@ class BlocksdirTest(BitcoinTestFramework):
self.start_node(0, ["-blocksdir=" + blocksdir_path])
self.log.info("mining blocks..")
self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address)
- assert os.path.isfile(os.path.join(blocksdir_path, "regtest", "blocks", "blk00000.dat"))
- assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks", "index"))
+ assert os.path.isfile(os.path.join(blocksdir_path, self.chain, "blocks", "blk00000.dat"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, self.chain, "blocks", "index"))
if __name__ == '__main__':
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index b16eafccca..e00219ca4a 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -57,16 +57,31 @@ def cltv_validate(node, tx, height):
class BIP65Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-whitelist=127.0.0.1', '-par=1']] # Use only one script thread to get the exact reject reason for testing
+ self.extra_args = [[
+ '-whitelist=127.0.0.1',
+ '-par=1', # Use only one script thread to get the exact reject reason for testing
+ '-acceptnonstdtxn=1', # cltv_invalidate is nonstandard
+ ]]
self.setup_clean_chain = True
self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_cltv_info(self, *, is_active):
+ assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'],
+ {
+ "active": is_active,
+ "height": CLTV_HEIGHT,
+ "type": "buried",
+ },
+ )
+
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
+ self.test_cltv_info(is_active=False)
+
self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2)]
self.nodeaddress = self.nodes[0].getnewaddress()
@@ -86,7 +101,9 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
+ self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 4")
@@ -135,7 +152,9 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
+ self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_cltv_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 2b93e3c24d..b997c76025 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -21,29 +21,45 @@ class ConfArgsTest(BitcoinTestFramework):
with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
conf.write('includeconf={}\n'.format(inc_conf_file_path))
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg='Error: Error parsing command line arguments: Invalid parameter -dash_cli',
+ extra_args=['-dash_cli=1'],
+ )
+ with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
+ conf.write('dash_conf=1\n')
+ with self.nodes[0].assert_debug_log(expected_msgs=['Ignoring unknown configuration value dash_conf']):
+ self.start_node(0)
+ self.stop_node(0)
+
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('-dash=1\n')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
- with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
- conf.write("wallet=foo\n")
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
+ if self.is_wallet_compiled():
+ with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
+ conf.write("wallet=foo\n")
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
+
+ with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
+ conf.write('regtest=0\n') # mainnet
+ conf.write('acceptnonstdtxn=1\n')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: acceptnonstdtxn is not currently supported for main chain')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('nono\n')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('server=1\nrpcuser=someuser\nmain.rpcpassword=some#pass')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
inc_conf_file2_path = os.path.join(self.nodes[0].datadir, 'include2.conf')
with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
@@ -61,9 +77,16 @@ class ConfArgsTest(BitcoinTestFramework):
with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
+ def test_log_buffer(self):
+ with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0']):
+ self.start_node(0, extra_args=['-noconnect=0'])
+ self.stop_node(0)
+
def run_test(self):
self.stop_node(0)
+ self.test_log_buffer()
+
self.test_config_file_parser()
# Remove the -datadir argument so it doesn't override the config file
@@ -86,17 +109,15 @@ class ConfArgsTest(BitcoinTestFramework):
f.write("datadir=" + new_data_dir + "\n")
f.write(conf_file_contents)
- # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
- #self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
+ self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error: Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
- # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
- #self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
- #self.stop_node(0)
- #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks'))
- #if self.is_wallet_compiled():
- #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
+ self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
+ self.stop_node(0)
+ assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks'))
+ if self.is_wallet_compiled():
+ assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
# Ensure command line argument overrides datadir in conf
os.mkdir(new_data_dir_2)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 887e9dafa3..6bd321992a 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -2,23 +2,17 @@
# Copyright (c) 2015-2019 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 activation of the first version bits soft fork.
+"""Test CSV soft fork activation.
This soft fork will activate the following BIPS:
BIP 68 - nSequence relative lock times
BIP 112 - CHECKSEQUENCEVERIFY
BIP 113 - MedianTimePast semantics for nLockTime
-regtest lock-in with 108/144 block signalling
-activation after a further 144 blocks
-
mine 82 blocks whose coinbases will be used to generate inputs for our tests
-mine 61 blocks to transition from DEFINED to STARTED
-mine 144 blocks only 100 of which are signaling readiness in order to fail to change state this period
-mine 144 blocks with 108 signaling and verify STARTED->LOCKED_IN
-mine 140 blocks and seed block chain with the 82 inputs will use for our tests at height 572
-mine 3 blocks and verify still at LOCKED_IN and test that enforcement has not triggered
-mine 1 block and test that enforcement has triggered (which triggers ACTIVE)
+mine 345 blocks and seed block chain with the 82 inputs will use for our tests at height 427
+mine 2 blocks and verify soft fork not yet activated
+mine 1 block and test that soft fork is activated (rules enforced for next block)
Test BIP 113 is enforced
Mine 4 blocks so next height is 580 and test BIP 68 is enforced for time and height
Mine 1 block so next height is 581 and test BIP 68 now passes time but not height
@@ -58,11 +52,12 @@ from test_framework.script import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- get_bip9_status,
hex_str_to_bytes,
+ softfork_active,
)
BASE_RELATIVE_LOCKTIME = 10
+CSV_ACTIVATION_HEIGHT = 432
SEQ_DISABLE_FLAG = 1 << 31
SEQ_RANDOM_HIGH_BIT = 1 << 25
SEQ_TYPE_FLAG = 1 << 22
@@ -148,20 +143,19 @@ class BIP68_112_113Test(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- def generate_blocks(self, number, version, test_blocks=None):
- if test_blocks is None:
- test_blocks = []
+ def generate_blocks(self, number):
+ test_blocks = []
for i in range(number):
- block = self.create_test_block([], version)
+ block = self.create_test_block([])
test_blocks.append(block)
self.last_block_time += 600
self.tip = block.sha256
self.tipheight += 1
return test_blocks
- def create_test_block(self, txs, version=536870912):
+ def create_test_block(self, txs):
block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600)
- block.nVersion = version
+ block.nVersion = 4
block.vtx.extend(txs)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
@@ -187,45 +181,14 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.tip = int(self.nodes[0].getbestblockhash(), 16)
self.nodeaddress = self.nodes[0].getnewaddress()
- self.log.info("Test that the csv softfork is DEFINED")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
- test_blocks = self.generate_blocks(61, 4)
- self.send_blocks(test_blocks)
-
- self.log.info("Advance from DEFINED to STARTED, height = 143")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
-
- self.log.info("Fail to achieve LOCKED_IN")
- # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks
-
- test_blocks = self.generate_blocks(50, 536870913) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not)
- self.send_blocks(test_blocks)
-
- self.log.info("Failed to advance past STARTED, height = 287")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
-
- self.log.info("Generate blocks to achieve LOCK-IN")
- # 108 out of 144 signal bit 0 to achieve lock-in
- # using a variety of bits to simulate multiple parallel softforks
- test_blocks = self.generate_blocks(58, 536870913) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not)
- self.send_blocks(test_blocks)
-
- self.log.info("Advanced from STARTED to LOCKED_IN, height = 431")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
-
- # Generate 140 more version 4 blocks
- test_blocks = self.generate_blocks(140, 4)
+ # Activation height is hardcoded
+ test_blocks = self.generate_blocks(345)
self.send_blocks(test_blocks)
+ assert not softfork_active(self.nodes[0], 'csv')
- # Inputs at height = 572
+ # Inputs at height = 431
#
- # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block)
+ # Put inputs for all tests in the chain at height 431 (tip now = 430) (time increases by 600s per block)
# Note we reuse inputs for v1 and v2 txs so must test these separately
# 16 normal inputs
bip68inputs = []
@@ -255,7 +218,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
self.nodes[0].setmocktime(self.last_block_time + 600)
- inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 572
+ inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 431
self.nodes[0].setmocktime(0)
self.tip = int(inputblockhash, 16)
self.tipheight += 1
@@ -263,11 +226,12 @@ class BIP68_112_113Test(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1)
# 2 more version 4 blocks
- test_blocks = self.generate_blocks(2, 4)
+ test_blocks = self.generate_blocks(2)
self.send_blocks(test_blocks)
- self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
+ assert_equal(self.tipheight, CSV_ACTIVATION_HEIGHT - 2)
+ self.log.info("Height = {}, CSV not yet active (will activate for block {}, not {})".format(self.tipheight, CSV_ACTIVATION_HEIGHT, CSV_ACTIVATION_HEIGHT - 1))
+ assert not softfork_active(self.nodes[0], 'csv')
# Test both version 1 and version 2 transactions for all tests
# BIP113 test transaction will be modified before each use to put in appropriate block time
@@ -340,10 +304,11 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
- test_blocks = self.generate_blocks(1, 4)
+ # 1 more version 4 block to get us to height 432 so the fork should now be active for the next block
+ assert not softfork_active(self.nodes[0], 'csv')
+ test_blocks = self.generate_blocks(1)
self.send_blocks(test_blocks)
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')
+ assert softfork_active(self.nodes[0], 'csv')
self.log.info("Post-Soft Fork Tests.")
@@ -364,8 +329,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.send_blocks([self.create_test_block([bip113tx])])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- # Next block height = 580 after 4 blocks of random version
- test_blocks = self.generate_blocks(4, 1234)
+ # Next block height = 437 after 4 blocks of random version
+ test_blocks = self.generate_blocks(4)
self.send_blocks(test_blocks)
self.log.info("BIP 68 tests")
@@ -392,8 +357,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
for tx in bip68heighttxs:
self.send_blocks([self.create_test_block([tx])], success=False)
- # Advance one block to 581
- test_blocks = self.generate_blocks(1, 1234)
+ # Advance one block to 438
+ test_blocks = self.generate_blocks(1)
self.send_blocks(test_blocks)
# Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
@@ -403,8 +368,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
for tx in bip68heighttxs:
self.send_blocks([self.create_test_block([tx])], success=False)
- # Advance one block to 582
- test_blocks = self.generate_blocks(1, 1234)
+ # Advance one block to 439
+ test_blocks = self.generate_blocks(1)
self.send_blocks(test_blocks)
# All BIP 68 txs should pass
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 62062926a6..6bd6bb5b8c 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -30,17 +30,27 @@ import http.client
import random
import time
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, ToHex
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ ToHex,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, create_confirmed_utxos, hex_str_to_bytes
+from test_framework.util import (
+ assert_equal,
+ create_confirmed_utxos,
+ hex_str_to_bytes,
+)
class ChainstateWriteCrashTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = False
- # Need a bit of extra time for the nodes to start up for this test
- self.rpc_timeout = 90
+ self.rpc_timeout = 480
# Set -maxmempool=0 to turn off mempool memory sharing with dbcache
# Set -rpcservertimeout=900 to reduce socket disconnects in this
@@ -48,13 +58,14 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"]
# Set different crash ratios and cache sizes. Note that not all of
- # -dbcache goes to pcoinsTip.
+ # -dbcache goes to the in-memory coins cache.
self.node0_args = ["-dbcrashratio=8", "-dbcache=4"] + self.base_args
self.node1_args = ["-dbcrashratio=16", "-dbcache=8"] + self.base_args
self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args
# Node3 is a normal node with default args, except will mine full blocks
- self.node3_args = ["-blockmaxweight=4000000"]
+ # and non-standard txs (e.g. txs with "dust" outputs)
+ self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
def skip_test_if_missing_module(self):
@@ -267,7 +278,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
# Warn if any of the nodes escaped restart.
for i in range(3):
if self.restart_counts[i] == 0:
- self.log.warn("Node %d never crashed during utxo flush!", i)
+ self.log.warning("Node %d never crashed during utxo flush!", i)
if __name__ == "__main__":
ChainstateWriteCrashTest().main()
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 7480e5c5ba..1bd9586364 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -51,9 +51,20 @@ class BIP66Test(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_dersig_info(self, *, is_active):
+ assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
+ {
+ "active": is_active,
+ "height": DERSIG_HEIGHT,
+ "type": "buried",
+ },
+ )
+
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
+ self.test_dersig_info(is_active=False)
+
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)]
self.nodeaddress = self.nodes[0].getnewaddress()
@@ -74,7 +85,9 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
+ self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 3")
@@ -128,8 +141,11 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
+ self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_dersig_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+
if __name__ == '__main__':
BIP66Test().main()
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index a4b9f213a1..d2d41b1206 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -28,6 +28,7 @@ P2SH_2 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_2), OP_EQUAL])
# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2
SCRIPT_SIG = [CScript([OP_TRUE, REDEEM_SCRIPT_1]), CScript([OP_TRUE, REDEEM_SCRIPT_2])]
+
def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment):
"""Create and send a transaction with a random fee.
@@ -69,6 +70,7 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
return (ToHex(tx), fee)
+
def split_inputs(from_node, txins, txouts, initial_split=False):
"""Generate a lot of inputs so we can generate a ton of transactions.
@@ -97,6 +99,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
+
def check_estimates(node, fees_seen):
"""Call estimatesmartfee and verify that the estimates meet certain invariants."""
@@ -120,9 +123,17 @@ def check_estimates(node, fees_seen):
else:
assert_greater_than_or_equal(i + 1, e["blocks"])
+
class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
+ # mine non-standard txs (e.g. txs with "dust" outputs)
+ # Force fSendTrickle to true (via whitelist)
+ self.extra_args = [
+ ["-acceptnonstdtxn", "-whitelist=127.0.0.1"],
+ ["-acceptnonstdtxn", "-whitelist=127.0.0.1", "-blockmaxweight=68000"],
+ ["-acceptnonstdtxn", "-whitelist=127.0.0.1", "-blockmaxweight=32000"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -133,9 +144,7 @@ class EstimateFeeTest(BitcoinTestFramework):
But first we need to use one node to create a lot of outputs
which we will use to generate our transactions.
"""
- self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
- ["-blockmaxweight=68000", "-maxorphantx=1000"],
- ["-blockmaxweight=32000", "-maxorphantx=1000"]])
+ self.add_nodes(3, extra_args=self.extra_args)
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.
# NOTE: the CreateNewBlock code starts counting block weight at 4,000 weight,
@@ -160,9 +169,9 @@ class EstimateFeeTest(BitcoinTestFramework):
self.memutxo, Decimal("0.005"), min_fee, min_fee)
tx_kbytes = (len(txhex) // 2) / 1000.0
self.fees_per_kb.append(float(fee) / tx_kbytes)
- self.sync_mempools(self.nodes[0:3], wait=.1)
+ self.sync_mempools(wait=.1)
mined = mining_node.getblock(mining_node.generate(1)[0], True)["tx"]
- self.sync_blocks(self.nodes[0:3], wait=.1)
+ self.sync_blocks(wait=.1)
# update which txouts are confirmed
newmem = []
for utx in self.memutxo:
@@ -184,22 +193,22 @@ class EstimateFeeTest(BitcoinTestFramework):
split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True)
# Mine
- while (len(self.nodes[0].getrawmempool()) > 0):
+ while len(self.nodes[0].getrawmempool()) > 0:
self.nodes[0].generate(1)
# Repeatedly split those 2 outputs, doubling twice for each rep
# Use txouts to monitor the available utxo, since these won't be tracked in wallet
reps = 0
- while (reps < 5):
+ while reps < 5:
# Double txouts to txouts2
- while (len(self.txouts) > 0):
+ while len(self.txouts) > 0:
split_inputs(self.nodes[0], self.txouts, self.txouts2)
- while (len(self.nodes[0].getrawmempool()) > 0):
+ while len(self.nodes[0].getrawmempool()) > 0:
self.nodes[0].generate(1)
# Double txouts2 to txouts
- while (len(self.txouts2) > 0):
+ while len(self.txouts2) > 0:
split_inputs(self.nodes[0], self.txouts2, self.txouts)
- while (len(self.nodes[0].getrawmempool()) > 0):
+ while len(self.nodes[0].getrawmempool()) > 0:
self.nodes[0].generate(1)
reps += 1
self.log.info("Finished splitting")
@@ -239,5 +248,6 @@ class EstimateFeeTest(BitcoinTestFramework):
self.log.info("Final estimates after emptying mempools")
check_estimates(self.nodes[1], self.fees_per_kb)
+
if __name__ == '__main__':
EstimateFeeTest().main()
diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py
index d06f6826f0..2cd6a05d08 100755
--- a/test/functional/feature_includeconf.py
+++ b/test/functional/feature_includeconf.py
@@ -43,7 +43,7 @@ class IncludeConfTest(BitcoinTestFramework):
self.log.info("-includeconf cannot be used as command-line arg")
self.stop_node(0)
- self.nodes[0].assert_start_raises_init_error(extra_args=["-includeconf=relative2.conf"], expected_msg="Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=relative2.conf")
+ self.nodes[0].assert_start_raises_init_error(extra_args=["-includeconf=relative2.conf"], expected_msg="Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=relative2.conf")
self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'")
with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f:
@@ -59,11 +59,11 @@ class IncludeConfTest(BitcoinTestFramework):
# Commented out as long as we ignore invalid arguments in configuration files
#with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f:
# f.write("foo=bar\n")
- #self.nodes[0].assert_start_raises_init_error(expected_msg="Error reading configuration file: Invalid configuration value foo")
+ #self.nodes[0].assert_start_raises_init_error(expected_msg="Error: Error reading configuration file: Invalid configuration value foo")
self.log.info("-includeconf cannot be invalid path")
os.remove(os.path.join(self.options.tmpdir, "node0", "relative.conf"))
- self.nodes[0].assert_start_raises_init_error(expected_msg="Error reading configuration file: Failed to include configuration file relative.conf")
+ self.nodes[0].assert_start_raises_init_error(expected_msg="Error: Error reading configuration file: Failed to include configuration file relative.conf")
self.log.info("multiple -includeconf args can be used from the base config file. subversion should end with 'main; relative; relative2)/'")
with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f:
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index 8bb7e02695..e6ff21ee9c 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -36,7 +36,7 @@ class LoggingTest(BitcoinTestFramework):
invdir = self.relative_log_path("foo")
invalidname = os.path.join("foo", "foo.log")
self.stop_node(0)
- exp_stderr = "Error: Could not open debug log file \S+$"
+ exp_stderr = r"Error: Could not open debug log file \S+$"
self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr, match=ErrorMatch.FULL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 87c318de9a..180ea0e51d 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -35,7 +35,7 @@ class MaxUploadTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-maxuploadtarget=800"]]
+ self.extra_args = [["-maxuploadtarget=800", "-acceptnonstdtxn=1"]]
# Cache for utxos, as the listunspent may take a long time later in the test
self.utxo_cache = []
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index a56c983ccc..250dee1528 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -41,7 +41,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
- self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy']]
+ self.extra_args = [['-whitelist=127.0.0.1', '-segwitheight=432', '-addresstype=legacy']]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -108,7 +108,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
witness and add_witness_commitment(block)
block.rehash()
block.solve()
- node.submitblock(block.serialize(True).hex())
+ node.submitblock(block.serialize().hex())
if (accept):
assert_equal(node.getbestblockhash(), block.hash)
self.tip = block.sha256
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index e2b3b2d544..51523f13e7 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -23,8 +23,6 @@ from test_framework.util import (
wait_until,
)
-MIN_BLOCKS_TO_KEEP = 288
-
# Rescans start at the earliest block up to 2 hours before a key timestamp, so
# the manual prune RPC avoids pruning blocks in the same window to be
# compatible with pruning based on key creation time.
@@ -94,6 +92,7 @@ class PruneTest(BitcoinTestFramework):
["-maxreceivebuffer=20000"],
["-prune=550"],
]
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -206,6 +205,7 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Mine 220 more large blocks so we have requisite history")
mine_large_blocks(self.nodes[0], 220)
+ self.sync_blocks(self.nodes[0:3], timeout=120)
usage = calc_usage(self.prunedir)
self.log.info("Usage should be below target: %d" % usage)
@@ -273,20 +273,9 @@ class PruneTest(BitcoinTestFramework):
else:
return index
- def prune(index, expected_ret=None):
+ def prune(index):
ret = node.pruneblockchain(height=height(index))
- # Check the return value. When use_timestamp is True, just check
- # that the return value is less than or equal to the expected
- # value, because when more than one block is generated per second,
- # a timestamp will not be granular enough to uniquely identify an
- # individual block.
- if expected_ret is None:
- expected_ret = index
- if use_timestamp:
- assert_greater_than(ret, 0)
- assert_greater_than(expected_ret + 1, ret)
- else:
- assert_equal(ret, expected_ret)
+ assert_equal(ret, node.getblockchaininfo()['pruneheight'])
def has_block(index):
return os.path.isfile(os.path.join(self.nodes[node_number].datadir, "regtest", "blocks", "blk{:05}.dat".format(index)))
@@ -326,7 +315,7 @@ class PruneTest(BitcoinTestFramework):
assert not has_block(1), "blk00001.dat is still there, should be pruned by now"
# height=1000 should not prune anything more, because tip-288 is in blk00002.dat.
- prune(1000, 1001 - MIN_BLOCKS_TO_KEEP)
+ prune(1000)
assert has_block(2), "blk00002.dat is still there, should be pruned by now"
# advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index ccba547a1c..fd79df0b07 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -64,19 +64,16 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
class ReplaceByFeeTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 2
+ self.num_nodes = 1
self.extra_args = [
[
+ "-acceptnonstdtxn=1",
"-maxorphantx=1000",
- "-whitelist=127.0.0.1",
"-limitancestorcount=50",
"-limitancestorsize=101",
"-limitdescendantcount=200",
"-limitdescendantsize=101",
],
- [
- "-mempoolreplacement=0",
- ],
]
def skip_test_if_missing_module(self):
@@ -148,16 +145,12 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
- # This will raise an exception due to transaction replacement being disabled
- assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, 0)
# Extra 0.1 BTC fee
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))]
tx1b_hex = txToHex(tx1b)
- # Replacement still disabled even with "enough fee"
- assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, 0)
# Works when enabled
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
@@ -168,11 +161,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid))
- # Second node is running mempoolreplacement=0, will not replace originally-seen txn
- mempool = self.nodes[1].getrawmempool()
- assert tx1a_txid in mempool
- assert tx1b_txid not in mempool
-
def test_doublespend_chain(self):
"""Doublespend of a long chain"""
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2d4dd96a1d..b9db618575 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -53,19 +53,22 @@ class SegWitTest(BitcoinTestFramework):
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
[
+ "-acceptnonstdtxn=1",
"-rpcserialversion=0",
- "-vbparams=segwit:0:999999999999",
+ "-segwitheight=432",
"-addresstype=legacy",
],
[
+ "-acceptnonstdtxn=1",
"-blockversion=4",
"-rpcserialversion=1",
- "-vbparams=segwit:0:999999999999",
+ "-segwitheight=432",
"-addresstype=legacy",
],
[
+ "-acceptnonstdtxn=1",
"-blockversion=536870915",
- "-vbparams=segwit:0:999999999999",
+ "-segwitheight=432",
"-addresstype=legacy",
],
]
@@ -223,6 +226,16 @@ class SegWitTest(BitcoinTestFramework):
assert tx.wit.is_null() # This should not be a segwit input
assert txid1 in self.nodes[0].getrawmempool()
+ tx1_hex = self.nodes[0].gettransaction(txid1)['hex']
+ tx1 = FromHex(CTransaction(), tx1_hex)
+
+ # Check that wtxid is properly reported in mempool entry (txid1)
+ assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True))
+
+ # Check that weight and vsize are properly reported in mempool entry (txid1)
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness())*3 + len(tx1.serialize_with_witness()))
+
# Now create tx2, which will spend from txid1.
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b''))
@@ -232,6 +245,13 @@ class SegWitTest(BitcoinTestFramework):
tx = FromHex(CTransaction(), tx2_hex)
assert not tx.wit.is_null()
+ # Check that wtxid is properly reported in mempool entry (txid2)
+ assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True))
+
+ # Check that weight and vsize are properly reported in mempool entry (txid2)
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+
# Now create tx3, which will spend from txid2
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b""))
@@ -248,9 +268,13 @@ class SegWitTest(BitcoinTestFramework):
assert txid2 in template_txids
assert txid3 in template_txids
- # Check that wtxid is properly reported in mempool entry
+ # Check that wtxid is properly reported in mempool entry (txid3)
assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True))
+ # Check that weight and vsize are properly reported in mempool entry (txid3)
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+
# Mine a block to clear the gbt cache again.
self.nodes[0].generate(1)
diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py
index fb4ad21359..85c250173f 100755
--- a/test/functional/feature_uacomment.py
+++ b/test/functional/feature_uacomment.py
@@ -27,12 +27,12 @@ class UacommentTest(BitcoinTestFramework):
self.log.info("test -uacomment max length")
self.stop_node(0)
- expected = "Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
+ expected = r"Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX)
self.log.info("test -uacomment unsafe characters")
for unsafe_char in ['/', ':', '(', ')', '₿', '🏃']:
- expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters."
+ expected = r"Error: User Agent comment \(" + re.escape(unsafe_char) + r"\) contains unsafe characters."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX)
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 7bb7044cc0..0a378c5ef5 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -29,7 +29,7 @@ class TestBitcoinCli(BitcoinTestFramework):
rpc_response = self.nodes[0].getblockchaininfo()
assert_equal(cli_response, rpc_response)
- user, password = get_auth_cookie(self.nodes[0].datadir)
+ user, password = get_auth_cookie(self.nodes[0].datadir, self.chain)
self.log.info("Test -stdinrpcpass option")
assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount())
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index 49ae0fb1a9..e99fa22646 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Tests some generic aspects of the RPC interface."""
+import os
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than_or_equal
@@ -31,6 +32,7 @@ class RPCInterfaceTest(BitcoinTestFramework):
command = info['active_commands'][0]
assert_equal(command['method'], 'getrpcinfo')
assert_greater_than_or_equal(command['duration'], 0)
+ assert_equal(info['logpath'], os.path.join(self.nodes[0].datadir, 'regtest', 'debug.log'))
def test_batch_request(self):
self.log.info("Testing basic JSON-RPC batch request...")
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 8e58c85c15..5aea10fbce 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -7,14 +7,13 @@ import struct
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction
-from test_framework.util import (
- assert_equal,
- hash256,
-)
+from test_framework.messages import CTransaction, hash256
+from test_framework.util import assert_equal, connect_nodes
from io import BytesIO
+from time import sleep
-ADDRESS = "tcp://127.0.0.1:28332"
+def hash256_reversed(byte_str):
+ return hash256(byte_str)[::-1]
class ZMQSubscriber:
def __init__(self, socket, topic):
@@ -43,67 +42,65 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_py3_zmq()
self.skip_if_no_bitcoind_zmq()
- def setup_nodes(self):
+ def run_test(self):
import zmq
+ self.ctx = zmq.Context()
+ try:
+ self.test_basic()
+ self.test_reorg()
+ finally:
+ # Destroy the ZMQ context.
+ self.log.debug("Destroying ZMQ context")
+ self.ctx.destroy(linger=None)
- # Initialize ZMQ context and socket.
+ def test_basic(self):
# All messages are received in the same socket which means
# that this test fails if the publishing order changes.
# Note that the publishing order is not defined in the documentation and
# is subject to change.
- self.zmq_context = zmq.Context()
- socket = self.zmq_context.socket(zmq.SUB)
+ import zmq
+ address = 'tcp://127.0.0.1:28332'
+ socket = self.ctx.socket(zmq.SUB)
socket.set(zmq.RCVTIMEO, 60000)
- socket.connect(ADDRESS)
# Subscribe to all available topics.
- self.hashblock = ZMQSubscriber(socket, b"hashblock")
- self.hashtx = ZMQSubscriber(socket, b"hashtx")
- self.rawblock = ZMQSubscriber(socket, b"rawblock")
- self.rawtx = ZMQSubscriber(socket, b"rawtx")
-
- self.extra_args = [
- ["-zmqpub%s=%s" % (sub.topic.decode(), ADDRESS) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]],
- [],
- ]
- self.add_nodes(self.num_nodes, self.extra_args)
- self.start_nodes()
- self.import_deterministic_coinbase_privkeys()
+ hashblock = ZMQSubscriber(socket, b"hashblock")
+ hashtx = ZMQSubscriber(socket, b"hashtx")
+ rawblock = ZMQSubscriber(socket, b"rawblock")
+ rawtx = ZMQSubscriber(socket, b"rawtx")
- def run_test(self):
- try:
- self._zmq_test()
- finally:
- # Destroy the ZMQ context.
- self.log.debug("Destroying ZMQ context")
- self.zmq_context.destroy(linger=None)
+ self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx]])
+ connect_nodes(self.nodes[0], 1)
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
- def _zmq_test(self):
num_blocks = 5
self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks})
genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE)
+
self.sync_all()
for x in range(num_blocks):
# Should receive the coinbase txid.
- txid = self.hashtx.receive()
+ txid = hashtx.receive()
# Should receive the coinbase raw transaction.
- hex = self.rawtx.receive()
+ hex = rawtx.receive()
tx = CTransaction()
tx.deserialize(BytesIO(hex))
tx.calc_sha256()
assert_equal(tx.hash, txid.hex())
# Should receive the generated block hash.
- hash = self.hashblock.receive().hex()
+ hash = hashblock.receive().hex()
assert_equal(genhashes[x], hash)
# The block should only have the coinbase txid.
assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
# Should receive the generated raw block.
- block = self.rawblock.receive()
- assert_equal(genhashes[x], hash256(block[:80]).hex())
+ block = rawblock.receive()
+ assert_equal(genhashes[x], hash256_reversed(block[:80]).hex())
if self.is_wallet_compiled():
self.log.info("Wait for tx from second node")
@@ -111,23 +108,49 @@ class ZMQTest (BitcoinTestFramework):
self.sync_all()
# Should receive the broadcasted txid.
- txid = self.hashtx.receive()
+ txid = hashtx.receive()
assert_equal(payment_txid, txid.hex())
# Should receive the broadcasted raw transaction.
- hex = self.rawtx.receive()
- assert_equal(payment_txid, hash256(hex).hex())
+ hex = rawtx.receive()
+ assert_equal(payment_txid, hash256_reversed(hex).hex())
self.log.info("Test the getzmqnotifications RPC")
assert_equal(self.nodes[0].getzmqnotifications(), [
- {"type": "pubhashblock", "address": ADDRESS, "hwm": 1000},
- {"type": "pubhashtx", "address": ADDRESS, "hwm": 1000},
- {"type": "pubrawblock", "address": ADDRESS, "hwm": 1000},
- {"type": "pubrawtx", "address": ADDRESS, "hwm": 1000},
+ {"type": "pubhashblock", "address": address, "hwm": 1000},
+ {"type": "pubhashtx", "address": address, "hwm": 1000},
+ {"type": "pubrawblock", "address": address, "hwm": 1000},
+ {"type": "pubrawtx", "address": address, "hwm": 1000},
])
assert_equal(self.nodes[1].getzmqnotifications(), [])
+ def test_reorg(self):
+ import zmq
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ hashblock = ZMQSubscriber(socket, b'hashblock')
+
+ # Should only notify the tip if a reorg occurs
+ self.restart_node(0, ['-zmqpub%s=%s' % (hashblock.topic.decode(), address)])
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ assert_equal(self.nodes[0].getbestblockhash(), hashblock.receive().hex())
+
+ # Generate 2 blocks in nodes[1]
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
+
+ # nodes[0] will reorg chain after connecting back nodes[1]
+ connect_nodes(self.nodes[0], 1)
+
+ # Should receive nodes[1] tip
+ assert_equal(self.nodes[1].getbestblockhash(), hashblock.receive().hex())
+
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index a94187ab90..e19e249f21 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -36,7 +36,6 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [[
'-txindex',
- '-acceptnonstdtxn=0', # Try to mimic main-net
]] * self.num_nodes
def skip_test_if_missing_module(self):
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index 351b27e94a..edf2069933 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -13,7 +13,11 @@ class MempoolLimitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]]
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ "-maxmempool=5",
+ "-spendzeroconfchange=0",
+ ]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
new file mode 100755
index 0000000000..0739d7e29b
--- /dev/null
+++ b/test/functional/mempool_package_onemore.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-2019 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 descendant package tracking carve-out allowing one final transaction in
+ an otherwise-full package as long as it has only one parent and is <= 10k in
+ size.
+"""
+
+from decimal import Decimal
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
+
+MAX_ANCESTORS = 25
+MAX_DESCENDANTS = 25
+
+class MempoolPackagesTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.extra_args = [["-maxorphantx=1000"]]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ # Build a transaction that spends parent_txid:vout
+ # Return amount sent
+ def chain_transaction(self, node, parent_txids, vouts, value, fee, num_outputs):
+ send_value = satoshi_round((value - fee)/num_outputs)
+ inputs = []
+ for (txid, vout) in zip(parent_txids, vouts):
+ inputs.append({'txid' : txid, 'vout' : vout})
+ outputs = {}
+ for i in range(num_outputs):
+ outputs[node.getnewaddress()] = send_value
+ rawtx = node.createrawtransaction(inputs, outputs, 0, True)
+ signedtx = node.signrawtransactionwithwallet(rawtx)
+ txid = node.sendrawtransaction(signedtx['hex'])
+ fulltx = node.getrawtransaction(txid, 1)
+ assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
+ return (txid, send_value)
+
+ def run_test(self):
+ # Mine some blocks and have them mature.
+ self.nodes[0].generate(101)
+ utxo = self.nodes[0].listunspent(10)
+ txid = utxo[0]['txid']
+ vout = utxo[0]['vout']
+ value = utxo[0]['amount']
+
+ fee = Decimal("0.0002")
+ # MAX_ANCESTORS transactions off a confirmed tx should be fine
+ chain = []
+ for _ in range(4):
+ (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
+ vout = 0
+ value = sent_value
+ chain.append([txid, value])
+ for _ in range(MAX_ANCESTORS - 4):
+ (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
+ value = sent_value
+ chain.append([txid, value])
+ (second_chain, second_chain_value) = self.chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
+
+ # Check mempool has MAX_ANCESTORS + 1 transactions in it
+ assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 1)
+
+ # Adding one more transaction on to the chain should fail.
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", self.chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
+ # ...even if it chains on from some point in the middle of the chain.
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
+ # ...even if it chains on to two parent transactions with one in the chain.
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
+ # ...especially if its > 40k weight
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
+ # But not if it chains directly off the first transaction
+ (replacable_txid, replacable_orig_value) = self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ # and the second chain should work just fine
+ self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+
+ # Make sure we can RBF the chain which used our carve-out rule
+ second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['addresses'][0]: replacable_orig_value - (Decimal(1) / Decimal(100))}
+ second_tx = self.nodes[0].createrawtransaction([{'txid': chain[0][0], 'vout': 1}], second_tx_outputs)
+ signed_second_tx = self.nodes[0].signrawtransactionwithwallet(second_tx)
+ self.nodes[0].sendrawtransaction(signed_second_tx['hex'])
+
+ # Finally, check that we added two transactions
+ assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 3)
+
+if __name__ == '__main__':
+ MempoolPackagesTest().main()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index b0a069be81..7e05a8e6c8 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -14,7 +14,10 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [["-printpriority=1"], ["-printpriority=1"]]
+ self.extra_args = [[
+ "-printpriority=1",
+ "-acceptnonstdtxn=1",
+ ]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 12cb06a407..3258a38e3c 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -19,7 +19,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
- self.log.info('Check that txs from p2p are rejected')
+ self.log.info('Check that txs from p2p are rejected and result in disconnect')
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
rawtx = self.nodes[0].createrawtransaction(
inputs=[{
@@ -42,13 +42,17 @@ class P2PBlocksOnly(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.nodes[0].p2p.sync_with_ping()
+ self.nodes[0].p2p.wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
+ # Remove the disconnected peer and add a new one.
+ del self.nodes[0].p2ps[0]
+ self.nodes[0].add_p2p_connection(P2PInterface())
+
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
- with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
+ with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]):
self.nodes[0].sendrawtransaction(sigtx)
self.nodes[0].p2p.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 3ca6bec254..7905cf5018 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -10,11 +10,11 @@ Version 2 compact blocks are post-segwit (wtxids)
import random
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
-from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, FromHex, HeaderAndShortIDs, msg_block, msg_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, msg_witness_block, msg_witness_blocktxn, MSG_WITNESS_FLAG, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ser_uint256, ToHex
+from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, FromHex, HeaderAndShortIDs, msg_no_witness_block, msg_no_witness_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, msg_block, msg_blocktxn, MSG_WITNESS_FLAG, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ser_uint256, ToHex
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.script import CScript, OP_TRUE, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, get_bip9_status, wait_until
+from test_framework.util import assert_equal, wait_until, softfork_active
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
@@ -95,6 +95,9 @@ class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ ]]
self.utxos = []
def skip_test_if_missing_module(self):
@@ -114,7 +117,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Create 10 more anyone-can-spend utxo's for testing.
def make_utxos(self):
block = self.build_block_on_tip(self.nodes[0])
- self.segwit_node.send_and_ping(msg_block(block))
+ self.segwit_node.send_and_ping(msg_no_witness_block(block))
assert int(self.nodes[0].getbestblockhash(), 16) == block.sha256
self.nodes[0].generatetoaddress(100, self.nodes[0].getnewaddress(address_type="bech32"))
@@ -130,7 +133,7 @@ class CompactBlocksTest(BitcoinTestFramework):
block2.vtx.append(tx)
block2.hashMerkleRoot = block2.calc_merkle_root()
block2.solve()
- self.segwit_node.send_and_ping(msg_block(block2))
+ self.segwit_node.send_and_ping(msg_no_witness_block(block2))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block2.sha256)
self.utxos.extend([[tx.sha256, i, out_value] for i in range(10)])
@@ -408,9 +411,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Send the coinbase, and verify that the tip advances.
if version == 2:
- msg = msg_witness_blocktxn()
- else:
msg = msg_blocktxn()
+ else:
+ msg = msg_no_witness_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = [block.vtx[0]]
test_node.send_and_ping(msg)
@@ -463,9 +466,9 @@ class CompactBlocksTest(BitcoinTestFramework):
test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])
- msg_bt = msg_blocktxn()
+ msg_bt = msg_no_witness_blocktxn()
if with_witness:
- msg_bt = msg_witness_blocktxn() # serialize with witnesses
+ msg_bt = msg_blocktxn() # serialize with witnesses
msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
test_tip_after_message(node, test_node, msg_bt, block.sha256)
@@ -554,9 +557,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# different peer provide the block further down, so that we're still
# verifying that the block isn't marked bad permanently. This is good
# enough for now.
- msg = msg_blocktxn()
+ msg = msg_no_witness_blocktxn()
if version == 2:
- msg = msg_witness_blocktxn()
+ msg = msg_blocktxn()
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
test_node.send_and_ping(msg)
@@ -571,9 +574,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Deliver the block
if version == 2:
- test_node.send_and_ping(msg_witness_block(block))
- else:
test_node.send_and_ping(msg_block(block))
+ else:
+ test_node.send_and_ping(msg_no_witness_block(block))
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def test_getblocktxn_handler(self, test_node):
@@ -785,7 +788,7 @@ class CompactBlocksTest(BitcoinTestFramework):
delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
assert int(node.getbestblockhash(), 16) != block.sha256
- msg = msg_blocktxn()
+ msg = msg_no_witness_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = block.vtx[1:]
stalling_peer.send_and_ping(msg)
@@ -800,7 +803,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# We will need UTXOs to construct transactions in later tests.
self.make_utxos()
- assert_equal(get_bip9_status(self.nodes[0], "segwit")["status"], 'active')
+ assert softfork_active(self.nodes[0], "segwit")
self.log.info("Testing SENDCMPCT p2p message... ")
self.test_sendcmpct(self.segwit_node, old_node=self.old_node)
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
new file mode 100755
index 0000000000..7d386c47f6
--- /dev/null
+++ b/test/functional/p2p_dos_header_tree.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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.messages import (
+ CBlockHeader,
+ FromHex,
+)
+from test_framework.mininode import (
+ P2PInterface,
+ msg_headers,
+)
+from test_framework.test_framework import BitcoinTestFramework
+
+import os
+
+
+class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.chain = 'testnet3' # Use testnet chain because it has an early checkpoint
+ self.num_nodes = 2
+
+ def add_options(self, parser):
+ parser.add_argument(
+ '--datafile',
+ default='data/blockheader_testnet3.hex',
+ help='Test data file (default: %(default)s)',
+ )
+
+ def run_test(self):
+ self.log.info("Read headers data")
+ self.headers_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
+ with open(self.headers_file_path, encoding='utf-8') as headers_data:
+ h_lines = [l.strip() for l in headers_data.readlines()]
+
+ # The headers data is taken from testnet3 for early blocks from genesis until the first checkpoint. There are
+ # two headers with valid POW at height 1 and 2, forking off from genesis. They are indicated by the FORK_PREFIX.
+ FORK_PREFIX = 'fork:'
+ self.headers = [l for l in h_lines if not l.startswith(FORK_PREFIX)]
+ self.headers_fork = [l[len(FORK_PREFIX):] for l in h_lines if l.startswith(FORK_PREFIX)]
+
+ self.headers = [FromHex(CBlockHeader(), h) for h in self.headers]
+ self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork]
+
+ self.log.info("Feed all non-fork headers, including and up to the first checkpoint")
+ self.nodes[0].add_p2p_connection(P2PInterface())
+ self.nodes[0].p2p.send_message(msg_headers(self.headers))
+ self.nodes[0].p2p.sync_with_ping()
+ assert {
+ 'height': 546,
+ 'hash': '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70',
+ 'branchlen': 546,
+ 'status': 'headers-only',
+ } in self.nodes[0].getchaintips()
+
+ self.log.info("Feed all fork headers (fails due to checkpoint)")
+ with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint (code 67)']):
+ self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
+ self.nodes[0].p2p.wait_for_disconnect()
+
+ 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.nodes[0].add_p2p_connection(P2PInterface())
+ self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
+ self.nodes[0].p2p.sync_with_ping()
+ assert {
+ "height": 2,
+ "hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
+ "branchlen": 2,
+ "status": "headers-only",
+ } in self.nodes[0].getchaintips()
+
+ # On node 1 it succeeds because no checkpoint has been reached yet by a chain tip
+ self.nodes[1].add_p2p_connection(P2PInterface())
+ self.nodes[1].p2p.send_message(msg_headers(self.headers_fork))
+ self.nodes[1].p2p.sync_with_ping()
+ assert {
+ "height": 2,
+ "hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
+ "branchlen": 2,
+ "status": "headers-only",
+ } in self.nodes[1].getchaintips()
+
+
+if __name__ == '__main__':
+ RejectLowDifficultyHeadersTest().main()
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index 481d697e63..58c72f89d8 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -6,6 +6,7 @@
import asyncio
import os
import struct
+import sys
from test_framework import messages
from test_framework.mininode import P2PDataStore, NetworkThread
@@ -84,7 +85,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
# Peer 1, despite serving up a bunch of nonsense, should still be connected.
self.log.info("Waiting for node to drop junk messages.")
- node.p2p.sync_with_ping(timeout=120)
+ node.p2p.sync_with_ping(timeout=320)
assert node.p2p.is_connected
#
@@ -92,18 +93,25 @@ class InvalidMessagesTest(BitcoinTestFramework):
#
# Send an oversized message, ensure we're disconnected.
#
- msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1))
- assert len(msg_over_size.serialize()) == (msg_limit + 1)
+ # Under macOS this test is skipped due to an unexpected error code
+ # returned from the closing socket which python/asyncio does not
+ # yet know how to handle.
+ #
+ if sys.platform != 'darwin':
+ msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1))
+ assert len(msg_over_size.serialize()) == (msg_limit + 1)
- with node.assert_debug_log(["Oversized message from peer=4, disconnecting"]):
- # An unknown message type (or *any* message type) over
- # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
- node.p2p.send_message(msg_over_size)
- node.p2p.wait_for_disconnect(timeout=4)
+ with node.assert_debug_log(["Oversized message from peer=4, disconnecting"]):
+ # An unknown message type (or *any* message type) over
+ # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
+ node.p2p.send_message(msg_over_size)
+ node.p2p.wait_for_disconnect(timeout=4)
- node.disconnect_p2ps()
- conn = node.add_p2p_connection(P2PDataStore())
- conn.wait_for_verack()
+ node.disconnect_p2ps()
+ conn = node.add_p2p_connection(P2PDataStore())
+ conn.wait_for_verack()
+ else:
+ self.log.info("Skipping test p2p_invalid_messages/1 (oversized message) under macOS")
#
# 2.
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index 1b18dd3e58..3cca2d78db 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -25,6 +25,9 @@ from data import invalid_txs
class InvalidTxRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ ]]
self.setup_clean_chain = True
def bootstrap_p2p(self, *, num_connections=1):
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 573d5f5a5f..a4650df8ee 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -8,7 +8,7 @@ Tests that a node configured with -prune=550 signals NODE_NETWORK_LIMITED correc
and that it responds to getdata requests for blocks correctly:
- send a block within 288 + 2 of the tip
- disconnect peers who request blocks older than that."""
-from test_framework.messages import CInv, msg_getdata, msg_verack, NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS
+from test_framework.messages import CInv, msg_getdata, msg_verack, NODE_NETWORK_LIMITED, NODE_WITNESS
from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -55,7 +55,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
- expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED
+ expected_services = NODE_WITNESS | NODE_NETWORK_LIMITED
self.log.info("Check that node has signalled expected services.")
assert_equal(node.nServices, expected_services)
@@ -83,7 +83,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
node1.wait_for_addr()
#must relay address with NODE_NETWORK_LIMITED
- assert_equal(node1.firstAddrnServices, 1036)
+ assert_equal(node1.firstAddrnServices, expected_services)
self.nodes[0].disconnect_p2ps()
node1.wait_for_disconnect()
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
new file mode 100755
index 0000000000..40b28d7533
--- /dev/null
+++ b/test/functional/p2p_permissions.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2018 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 p2p permission message.
+
+Test that permissions are correctly calculated and applied
+"""
+
+from test_framework.test_node import ErrorMatch
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ p2p_port,
+)
+
+class P2PPermissionsTests(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [[],[]]
+
+ def run_test(self):
+ self.checkpermission(
+ # default permissions (no specific permissions)
+ ["-whitelist=127.0.0.1"],
+ ["relay", "noban", "mempool"],
+ True)
+
+ self.checkpermission(
+ # relay permission removed (no specific permissions)
+ ["-whitelist=127.0.0.1", "-whitelistrelay=0"],
+ ["noban", "mempool"],
+ True)
+
+ self.checkpermission(
+ # forcerelay and relay permission added
+ # Legacy parameter interaction which set whitelistrelay to true
+ # if whitelistforcerelay is true
+ ["-whitelist=127.0.0.1", "-whitelistforcerelay"],
+ ["forcerelay", "relay", "noban", "mempool"],
+ True)
+
+ # Let's make sure permissions are merged correctly
+ # For this, we need to use whitebind instead of bind
+ # by modifying the configuration file.
+ ip_port = "127.0.0.1:{}".format(p2p_port(1))
+ self.replaceinconfig(1, "bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)
+ self.checkpermission(
+ ["-whitelist=noban@127.0.0.1" ],
+ # Check parameter interaction forcerelay should activate relay
+ ["noban", "bloomfilter", "forcerelay", "relay" ],
+ False)
+ self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")
+
+ self.checkpermission(
+ # legacy whitelistrelay should be ignored
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
+ ["noban", "mempool"],
+ False)
+
+ self.checkpermission(
+ # legacy whitelistforcerelay should be ignored
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
+ ["noban", "mempool"],
+ False)
+
+ self.checkpermission(
+ # missing mempool permission to be considered legacy whitelisted
+ ["-whitelist=noban@127.0.0.1"],
+ ["noban"],
+ False)
+
+ self.checkpermission(
+ # all permission added
+ ["-whitelist=all@127.0.0.1"],
+ ["forcerelay", "noban", "mempool", "bloomfilter", "relay"],
+ False)
+
+ self.stop_node(1)
+ self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX)
+
+ def checkpermission(self, args, expectedPermissions, whitelisted):
+ self.restart_node(1, args)
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert_equal(peerinfo['whitelisted'], whitelisted)
+ assert_equal(len(expectedPermissions), len(peerinfo['permissions']))
+ for p in expectedPermissions:
+ if not p in peerinfo['permissions']:
+ raise AssertionError("Expected permissions %r is not granted." % p)
+
+ def replaceinconfig(self, nodeid, old, new):
+ with open(self.nodes[nodeid].bitcoinconf, encoding="utf8") as f:
+ newText=f.read().replace(old, new)
+ with open(self.nodes[nodeid].bitcoinconf, 'w', encoding="utf8") as f:
+ f.write(newText)
+
+if __name__ == '__main__':
+ P2PPermissionsTests().main()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index df0f9fa365..98f6b1d71d 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -25,12 +25,12 @@ from test_framework.messages import (
MSG_WITNESS_FLAG,
NODE_NETWORK,
NODE_WITNESS,
- msg_block,
+ msg_no_witness_block,
msg_getdata,
msg_headers,
msg_inv,
msg_tx,
- msg_witness_block,
+ msg_block,
msg_witness_tx,
ser_uint256,
ser_vector,
@@ -76,7 +76,7 @@ from test_framework.util import (
assert_equal,
connect_nodes,
disconnect_nodes,
- get_bip9_status,
+ softfork_active,
hex_str_to_bytes,
assert_raises_rpc_error,
)
@@ -88,6 +88,8 @@ VB_TOP_BITS = 0x20000000
MAX_SIGOP_COST = 80000
+SEGWIT_HEIGHT = 120
+
class UTXO():
"""Used to keep track of anyone-can-spend outputs that we can use in the tests."""
def __init__(self, sha256, n, value):
@@ -111,7 +113,7 @@ def get_virtual_size(witness_block):
Virtual size is base + witness/4."""
base_size = len(witness_block.serialize(with_witness=False))
- total_size = len(witness_block.serialize(with_witness=True))
+ total_size = len(witness_block.serialize())
# the "+3" is so we round up
vsize = int((3 * base_size + total_size + 3) / 4)
return vsize
@@ -134,7 +136,7 @@ def test_witness_block(node, p2p, block, accepted, with_witness=True, reason=Non
- use the getbestblockhash rpc to check for acceptance."""
reason = [reason] if reason else []
with node.assert_debug_log(expected_msgs=reason):
- p2p.send_message(msg_witness_block(block) if with_witness else msg_block(block))
+ p2p.send_message(msg_block(block) if with_witness else msg_no_witness_block(block))
p2p.sync_with_ping()
assert_equal(node.getbestblockhash() == block.hash, accepted)
@@ -184,7 +186,11 @@ class SegWitTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 3
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
- self.extra_args = [["-whitelist=127.0.0.1", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
+ self.extra_args = [
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight=-1"]
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -227,26 +233,18 @@ class SegWitTest(BitcoinTestFramework):
# Keep a place to store utxo's that can be used in later tests
self.utxo = []
- # Segwit status 'defined'
- self.segwit_status = 'defined'
+ self.log.info("Starting tests before segwit activation")
+ self.segwit_active = False
self.test_non_witness_transaction()
- self.test_unnecessary_witness_before_segwit_activation()
self.test_v0_outputs_arent_spendable()
self.test_block_relay()
- self.advance_to_segwit_started()
-
- # Segwit status 'started'
-
self.test_getblocktemplate_before_lockin()
- self.advance_to_segwit_lockin()
-
- # Segwit status 'locked_in'
-
self.test_unnecessary_witness_before_segwit_activation()
self.test_witness_tx_relay_before_segwit_activation()
- self.test_block_relay()
self.test_standardness_v0()
+
+ self.log.info("Advancing to segwit activation")
self.advance_to_segwit_active()
# Segwit status 'active'
@@ -278,15 +276,15 @@ class SegWitTest(BitcoinTestFramework):
def subtest(func): # noqa: N805
"""Wraps the subtests for logging and state assertions."""
def func_wrapper(self, *args, **kwargs):
- self.log.info("Subtest: {} (Segwit status = {})".format(func.__name__, self.segwit_status))
+ self.log.info("Subtest: {} (Segwit active = {})".format(func.__name__, self.segwit_active))
# Assert segwit status is as expected
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status)
+ assert_equal(softfork_active(self.nodes[0], 'segwit'), self.segwit_active)
func(self, *args, **kwargs)
# Each subtest should leave some utxos for the next subtest
assert self.utxo
self.sync_blocks()
# Assert segwit status is as expected at end of subtest
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status)
+ assert_equal(softfork_active(self.nodes[0], 'segwit'), self.segwit_active)
return func_wrapper
@@ -298,7 +296,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block(version=1)
block.solve()
- self.test_node.send_message(msg_block(block))
+ self.test_node.send_message(msg_no_witness_block(block))
self.test_node.sync_with_ping() # make sure the block was processed
txid = block.vtx[0].sha256
@@ -345,7 +343,7 @@ class SegWitTest(BitcoinTestFramework):
# But it should not be permanently marked bad...
# Resend without witness information.
- self.test_node.send_message(msg_block(block))
+ self.test_node.send_message(msg_no_witness_block(block))
self.test_node.sync_with_ping()
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -388,7 +386,7 @@ class SegWitTest(BitcoinTestFramework):
# Check that we can getdata for witness blocks or regular blocks,
# and the right thing happens.
- if self.segwit_status != 'active':
+ if not self.segwit_active:
# Before activation, we should be able to request old blocks with
# or without witness, and they should be the same.
chain_height = self.nodes[0].getblockcount()
@@ -403,7 +401,7 @@ class SegWitTest(BitcoinTestFramework):
block_hash = int(block_hash, 16)
block = self.test_node.request_block(block_hash, 2)
wit_block = self.test_node.request_block(block_hash, 2 | MSG_WITNESS_FLAG)
- assert_equal(block.serialize(True), wit_block.serialize(True))
+ assert_equal(block.serialize(), wit_block.serialize())
assert_equal(block.serialize(), hex_str_to_bytes(rpc_block))
else:
# After activation, witness blocks and non-witness blocks should
@@ -419,15 +417,15 @@ class SegWitTest(BitcoinTestFramework):
rpc_block = self.nodes[0].getblock(block.hash, False)
non_wit_block = self.test_node.request_block(block.sha256, 2)
wit_block = self.test_node.request_block(block.sha256, 2 | MSG_WITNESS_FLAG)
- assert_equal(wit_block.serialize(True), hex_str_to_bytes(rpc_block))
+ assert_equal(wit_block.serialize(), hex_str_to_bytes(rpc_block))
assert_equal(wit_block.serialize(False), non_wit_block.serialize())
- assert_equal(wit_block.serialize(True), block.serialize(True))
+ assert_equal(wit_block.serialize(), block.serialize())
# Test size, vsize, weight
rpc_details = self.nodes[0].getblock(block.hash, True)
- assert_equal(rpc_details["size"], len(block.serialize(True)))
+ assert_equal(rpc_details["size"], len(block.serialize()))
assert_equal(rpc_details["strippedsize"], len(block.serialize(False)))
- weight = 3 * len(block.serialize(False)) + len(block.serialize(True))
+ weight = 3 * len(block.serialize(False)) + len(block.serialize())
assert_equal(rpc_details["weight"], weight)
# Upgraded node should not ask for blocks from unupgraded
@@ -532,32 +530,18 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.append(UTXO(txid, 2, value))
@subtest
- def advance_to_segwit_started(self):
- """Mine enough blocks for segwit's vb state to be 'started'."""
- height = self.nodes[0].getblockcount()
- # Will need to rewrite the tests here if we are past the first period
- assert height < VB_PERIOD - 1
- # Advance to end of period, status should now be 'started'
- self.nodes[0].generate(VB_PERIOD - height - 1)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started')
- self.segwit_status = 'started'
-
- @subtest
def test_getblocktemplate_before_lockin(self):
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
for node in [self.nodes[0], self.nodes[2]]:
gbt_results = node.getblocktemplate({"rules": ["segwit"]})
- block_version = gbt_results['version']
if node == self.nodes[2]:
# If this is a non-segwit node, we should not get a witness
- # commitment, nor a version bit signalling segwit.
- assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
+ # commitment.
assert 'default_witness_commitment' not in gbt_results
else:
- # For segwit-aware nodes, check the version bit and the witness
- # commitment are correct.
- assert block_version & (1 << VB_WITNESS_BIT) != 0
+ # For segwit-aware nodes, check the witness
+ # commitment is correct.
assert 'default_witness_commitment' in gbt_results
witness_commitment = gbt_results['default_witness_commitment']
@@ -567,18 +551,9 @@ class SegWitTest(BitcoinTestFramework):
script = get_witness_script(witness_root, 0)
assert_equal(witness_commitment, script.hex())
- @subtest
- def advance_to_segwit_lockin(self):
- """Mine enough blocks to lock in segwit, but don't activate."""
- height = self.nodes[0].getblockcount()
- # Advance to end of period, and verify lock-in happens at the end
- self.nodes[0].generate(VB_PERIOD - 1)
- height = self.nodes[0].getblockcount()
- assert (height % VB_PERIOD) == VB_PERIOD - 2
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started')
+ # Clear out the mempool
self.nodes[0].generate(1)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in')
- self.segwit_status = 'locked_in'
+ self.sync_blocks()
@subtest
def test_witness_tx_relay_before_segwit_activation(self):
@@ -682,7 +657,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx3.rehash()
- if self.segwit_status != 'active':
+ if not self.segwit_active:
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
# in blocks and the tx is impossible to mine right now.
assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
@@ -703,12 +678,13 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def advance_to_segwit_active(self):
"""Mine enough blocks to activate segwit."""
+ assert not softfork_active(self.nodes[0], 'segwit')
height = self.nodes[0].getblockcount()
- self.nodes[0].generate(VB_PERIOD - (height % VB_PERIOD) - 2)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in')
+ self.nodes[0].generate(SEGWIT_HEIGHT - height - 2)
+ assert not softfork_active(self.nodes[0], 'segwit')
self.nodes[0].generate(1)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'active')
- self.segwit_status = 'active'
+ assert softfork_active(self.nodes[0], 'segwit')
+ self.segwit_active = True
@subtest
def test_p2sh_witness(self):
@@ -791,7 +767,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
# Test the test -- witness serialization should be different
- assert msg_witness_block(block).serialize() != msg_block(block).serialize()
+ assert msg_block(block).serialize() != msg_no_witness_block(block).serialize()
# This empty block should be valid.
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
@@ -884,13 +860,13 @@ class SegWitTest(BitcoinTestFramework):
# We can't send over the p2p network, because this is too big to relay
# TODO: repeat this test with a block that can be relayed
- self.nodes[0].submitblock(block.serialize(True).hex())
+ self.nodes[0].submitblock(block.serialize().hex())
assert self.nodes[0].getbestblockhash() != block.hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop()
assert get_virtual_size(block) < MAX_BLOCK_BASE_SIZE
- self.nodes[0].submitblock(block.serialize(True).hex())
+ self.nodes[0].submitblock(block.serialize().hex())
assert self.nodes[0].getbestblockhash() == block.hash
@@ -969,7 +945,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1)
# Make sure that our test case would exceed the old max-network-message
# limit
- assert len(block.serialize(True)) > 2 * 1024 * 1024
+ assert len(block.serialize()) > 2 * 1024 * 1024
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
@@ -997,14 +973,14 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block, nonce=1)
block.vtx[0].wit = CTxWitness() # drop the nonce
block.solve()
- self.nodes[0].submitblock(block.serialize(True).hex())
+ self.nodes[0].submitblock(block.serialize().hex())
assert self.nodes[0].getbestblockhash() != block.hash
# Now redo commitment with the standard nonce, but let bitcoind fill it in.
add_witness_commitment(block, nonce=0)
block.vtx[0].wit = CTxWitness()
block.solve()
- self.nodes[0].submitblock(block.serialize(True).hex())
+ self.nodes[0].submitblock(block.serialize().hex())
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
# This time, add a tx with non-empty witness, but don't supply
@@ -1019,7 +995,7 @@ class SegWitTest(BitcoinTestFramework):
block_2.vtx[0].vout.pop()
block_2.vtx[0].wit = CTxWitness()
- self.nodes[0].submitblock(block_2.serialize(True).hex())
+ self.nodes[0].submitblock(block_2.serialize().hex())
# Tip should not advance!
assert self.nodes[0].getbestblockhash() != block_2.hash
@@ -1920,13 +1896,13 @@ class SegWitTest(BitcoinTestFramework):
# Restart with the new binary
self.stop_node(2)
- self.start_node(2, extra_args=["-vbparams=segwit:0:999999999999"])
+ self.start_node(2, extra_args=["-segwitheight={}".format(SEGWIT_HEIGHT)])
connect_nodes(self.nodes[0], 2)
self.sync_blocks()
# Make sure that this peer thinks segwit has activated.
- assert get_bip9_status(self.nodes[2], 'segwit')['status'] == "active"
+ assert softfork_active(self.nodes[2], 'segwit')
# Make sure this peer's blocks match those of node0.
height = self.nodes[2].getblockcount()
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
new file mode 100755
index 0000000000..19d78ff303
--- /dev/null
+++ b/test/functional/p2p_tx_download.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 transaction download behavior
+"""
+
+from test_framework.messages import (
+ CInv,
+ CTransaction,
+ FromHex,
+ MSG_TX,
+ MSG_TYPE_MASK,
+ msg_inv,
+ msg_notfound,
+)
+from test_framework.mininode import (
+ P2PInterface,
+ mininode_lock,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ wait_until,
+)
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+
+import time
+
+
+class TestP2PConn(P2PInterface):
+ def __init__(self):
+ super().__init__()
+ self.tx_getdata_count = 0
+
+ def on_getdata(self, message):
+ for i in message.inv:
+ if i.type & MSG_TYPE_MASK == MSG_TX:
+ self.tx_getdata_count += 1
+
+
+# Constants from net_processing
+GETDATA_TX_INTERVAL = 60 # seconds
+MAX_GETDATA_RANDOM_DELAY = 2 # seconds
+INBOUND_PEER_TX_DELAY = 2 # seconds
+MAX_GETDATA_IN_FLIGHT = 100
+TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
+
+# Python test constants
+NUM_INBOUND = 10
+MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY
+
+
+class TxDownloadTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 2
+
+ def test_tx_requests(self):
+ self.log.info("Test that we request transactions from all our peers, eventually")
+
+ txid = 0xdeadbeef
+
+ self.log.info("Announce the txid from each incoming peer to node 0")
+ msg = msg_inv([CInv(t=1, h=txid)])
+ for p in self.nodes[0].p2ps:
+ p.send_message(msg)
+ p.sync_with_ping()
+
+ outstanding_peer_index = [i for i in range(len(self.nodes[0].p2ps))]
+
+ def getdata_found(peer_index):
+ p = self.nodes[0].p2ps[peer_index]
+ with mininode_lock:
+ return p.last_message.get("getdata") and p.last_message["getdata"].inv[-1].hash == txid
+
+ node_0_mocktime = int(time.time())
+ while outstanding_peer_index:
+ node_0_mocktime += MAX_GETDATA_INBOUND_WAIT
+ self.nodes[0].setmocktime(node_0_mocktime)
+ wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index))
+ for i in outstanding_peer_index:
+ if getdata_found(i):
+ outstanding_peer_index.remove(i)
+
+ self.nodes[0].setmocktime(0)
+ self.log.info("All outstanding peers received a getdata")
+
+ def test_inv_block(self):
+ self.log.info("Generate a transaction on node 0")
+ tx = self.nodes[0].createrawtransaction(
+ inputs=[{ # coinbase
+ "txid": self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0],
+ "vout": 0
+ }],
+ outputs={ADDRESS_BCRT1_UNSPENDABLE: 50 - 0.00025},
+ )
+ tx = self.nodes[0].signrawtransactionwithkey(
+ hexstring=tx,
+ privkeys=[self.nodes[0].get_deterministic_priv_key().key],
+ )['hex']
+ ctx = FromHex(CTransaction(), tx)
+ txid = int(ctx.rehash(), 16)
+
+ self.log.info(
+ "Announce the transaction to all nodes from all {} incoming peers, but never send it".format(NUM_INBOUND))
+ msg = msg_inv([CInv(t=1, h=txid)])
+ for p in self.peers:
+ p.send_message(msg)
+ p.sync_with_ping()
+
+ self.log.info("Put the tx in node 0's mempool")
+ self.nodes[0].sendrawtransaction(tx)
+
+ # Since node 1 is connected outbound to an honest peer (node 0), it
+ # should get the tx within a timeout. (Assuming that node 0
+ # announced the tx within the timeout)
+ # The timeout is the sum of
+ # * the worst case until the tx is first requested from an inbound
+ # peer, plus
+ # * the first time it is re-requested from the outbound peer, plus
+ # * 2 seconds to avoid races
+ timeout = 2 + (MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY) + (
+ GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY)
+ self.log.info("Tx should be received at node 1 after {} seconds".format(timeout))
+ self.sync_mempools(timeout=timeout)
+
+ def test_in_flight_max(self):
+ self.log.info("Test that we don't request more than {} transactions from any peer, every {} minutes".format(
+ MAX_GETDATA_IN_FLIGHT, TX_EXPIRY_INTERVAL / 60))
+ txids = [i for i in range(MAX_GETDATA_IN_FLIGHT + 2)]
+
+ p = self.nodes[0].p2ps[0]
+
+ with mininode_lock:
+ p.tx_getdata_count = 0
+
+ p.send_message(msg_inv([CInv(t=1, h=i) for i in txids]))
+ wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT)
+
+ self.log.info("Now check that if we send a NOTFOUND for a transaction, we'll get one more request")
+ p.send_message(msg_notfound(vec=[CInv(t=1, h=txids[0])]))
+ wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT + 1, timeout=10, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT + 1)
+
+ WAIT_TIME = TX_EXPIRY_INTERVAL // 2 + TX_EXPIRY_INTERVAL
+ self.log.info("if we wait about {} minutes, we should eventually get more requests".format(WAIT_TIME / 60))
+ self.nodes[0].setmocktime(int(time.time() + WAIT_TIME))
+ wait_until(lambda: p.tx_getdata_count == MAX_GETDATA_IN_FLIGHT + 2)
+ self.nodes[0].setmocktime(0)
+
+ def run_test(self):
+ # Setup the p2p connections
+ self.peers = []
+ for node in self.nodes:
+ for i in range(NUM_INBOUND):
+ self.peers.append(node.add_p2p_connection(TestP2PConn()))
+
+ self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
+
+ # Test the in-flight max first, because we want no transactions in
+ # flight ahead of this test.
+ self.test_in_flight_max()
+
+ self.test_inv_block()
+
+ self.test_tx_requests()
+
+
+if __name__ == '__main__':
+ TxDownloadTest().main()
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index acc7cd811c..8979251a26 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -55,7 +55,7 @@ class RPCBindTest(BitcoinTestFramework):
self.nodes[0].rpchost = None
self.start_nodes([node_args])
# connect to node through non-loopback interface
- node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
+ node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, self.chain, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
node.getnetworkinfo()
self.stop_nodes()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index facb05b54c..266a0d6cd2 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -78,7 +78,6 @@ class BlockchainTest(BitcoinTestFramework):
keys = [
'bestblockhash',
- 'bip9_softforks',
'blocks',
'chain',
'chainwork',
@@ -124,6 +123,31 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['prune_target_size'], 576716800)
assert_greater_than(res['size_on_disk'], 0)
+ assert_equal(res['softforks'], {
+ 'bip34': {'type': 'buried', 'active': False, 'height': 500},
+ 'bip66': {'type': 'buried', 'active': False, 'height': 1251},
+ 'bip65': {'type': 'buried', 'active': False, 'height': 1351},
+ 'csv': {'type': 'buried', 'active': False, 'height': 432},
+ 'segwit': {'type': 'buried', 'active': True, 'height': 0},
+ 'testdummy': {
+ 'type': 'bip9',
+ 'bip9': {
+ 'status': 'started',
+ 'bit': 28,
+ 'startTime': 0,
+ 'timeout': 0x7fffffffffffffff, # testdummy does not have a timeout so is set to the max int64 value
+ 'since': 144,
+ 'statistics': {
+ 'period': 144,
+ 'threshold': 108,
+ 'elapsed': 57,
+ 'count': 57,
+ 'possible': True,
+ },
+ },
+ 'active': False}
+ })
+
def _test_getchaintxstats(self):
self.log.info("Test getchaintxstats")
@@ -162,6 +186,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['time'], b200['time'])
assert_equal(chaintxstats['txcount'], 201)
assert_equal(chaintxstats['window_final_block_hash'], b200_hash)
+ assert_equal(chaintxstats['window_final_block_height'], 200)
assert_equal(chaintxstats['window_block_count'], 199)
assert_equal(chaintxstats['window_tx_count'], 199)
assert_equal(chaintxstats['window_interval'], time_diff)
@@ -171,6 +196,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['time'], b1['time'])
assert_equal(chaintxstats['txcount'], 2)
assert_equal(chaintxstats['window_final_block_hash'], b1_hash)
+ assert_equal(chaintxstats['window_final_block_height'], 1)
assert_equal(chaintxstats['window_block_count'], 0)
assert 'window_tx_count' not in chaintxstats
assert 'window_interval' not in chaintxstats
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 7abcd71bb8..056e193d55 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -7,9 +7,13 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_raises_rpc_error,
+ assert_equal,
)
-import decimal
+from test_framework.key import ECPubKey
+import binascii
+import decimal
+import itertools
class RpcCreateMultiSigTest(BitcoinTestFramework):
def set_test_params(self):
@@ -44,6 +48,30 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
self.checkbalances()
+ # Test mixed compressed and uncompressed pubkeys
+ self.log.info('Mixed compressed and uncompressed multisigs are not allowed')
+ pk0 = node0.getaddressinfo(node0.getnewaddress())['pubkey']
+ pk1 = node1.getaddressinfo(node1.getnewaddress())['pubkey']
+ pk2 = node2.getaddressinfo(node2.getnewaddress())['pubkey']
+
+ # decompress pk2
+ pk_obj = ECPubKey()
+ pk_obj.set(binascii.unhexlify(pk2))
+ pk_obj.compressed = False
+ pk2 = binascii.hexlify(pk_obj.get_bytes()).decode()
+
+ # Check all permutations of keys because order matters apparently
+ for keys in itertools.permutations([pk0, pk1, pk2]):
+ # Results should be the same as this legacy one
+ legacy_addr = node0.createmultisig(2, keys, 'legacy')['address']
+ assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'legacy')['address'])
+
+ # Generate addresses with the segwit types. These should all make legacy addresses
+ assert_equal(legacy_addr, node0.createmultisig(2, keys, 'bech32')['address'])
+ assert_equal(legacy_addr, node0.createmultisig(2, keys, 'p2sh-segwit')['address'])
+ assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'bech32')['address'])
+ assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'p2sh-segwit')['address'])
+
def check_addmultisigaddress_errors(self):
self.log.info('Check that addmultisigaddress fails when the private keys are missing')
addresses = [self.nodes[1].getnewaddress(address_type='legacy') for _ in range(2)]
@@ -101,6 +129,32 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
outval = value - decimal.Decimal("0.00001000")
rawtx = node2.createrawtransaction([{"txid": txid, "vout": vout}], [{self.final: outval}])
+ prevtx_err = dict(prevtxs[0])
+ del prevtx_err["redeemScript"]
+
+ assert_raises_rpc_error(-8, "Missing redeemScript/witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # if witnessScript specified, all ok
+ prevtx_err["witnessScript"] = prevtxs[0]["redeemScript"]
+ node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # both specified, also ok
+ prevtx_err["redeemScript"] = prevtxs[0]["redeemScript"]
+ node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # redeemScript mismatch to witnessScript
+ prevtx_err["redeemScript"] = "6a" # OP_RETURN
+ assert_raises_rpc_error(-8, "redeemScript does not correspond to witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # redeemScript does not match scriptPubKey
+ del prevtx_err["witnessScript"]
+ assert_raises_rpc_error(-8, "redeemScript/witnessScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # witnessScript does not match scriptPubKey
+ prevtx_err["witnessScript"] = prevtx_err["redeemScript"]
+ del prevtx_err["redeemScript"]
+ assert_raises_rpc_error(-8, "redeemScript/witnessScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py
index 1984694692..42128d5767 100755
--- a/test/functional/rpc_deriveaddresses.py
+++ b/test/functional/rpc_deriveaddresses.py
@@ -13,14 +13,14 @@ class DeriveaddressesTest(BitcoinTestFramework):
self.supports_cli = 1
def run_test(self):
- assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, "a")
+ assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, "a")
descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
assert_equal(self.nodes[0].deriveaddresses(descriptor), [address])
descriptor = descriptor[:-9]
- assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, descriptor)
+ assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, descriptor)
descriptor_pubkey = "wpkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)#s9ga3alw"
address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index d89fd6461f..b621081752 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -41,11 +41,11 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 3)
def run_test(self):
- min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
+ self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
- node.settxfee(min_relay_tx_fee)
+ node.settxfee(self.min_relay_tx_fee)
# if the fee's positive delta is higher than this value tests will fail,
# neg. delta always fail the tests.
@@ -53,13 +53,43 @@ class RawTransactionsTest(BitcoinTestFramework):
# than a minimum sized signature.
# = 2 bytes * minRelayTxFeePerByte
- feeTolerance = 2 * min_relay_tx_fee/1000
+ self.fee_tolerance = 2 * self.min_relay_tx_fee / 1000
self.nodes[2].generate(1)
self.sync_all()
self.nodes[0].generate(121)
self.sync_all()
+ self.test_change_position()
+ self.test_simple()
+ self.test_simple_two_coins()
+ self.test_simple_two_outputs()
+ self.test_change()
+ self.test_no_change()
+ self.test_invalid_option()
+ self.test_invalid_change_address()
+ self.test_valid_change_address()
+ self.test_change_type()
+ self.test_coin_selection()
+ self.test_two_vin()
+ self.test_two_vin_two_vout()
+ self.test_invalid_input()
+ self.test_fee_p2pkh()
+ self.test_fee_p2pkh_multi_out()
+ self.test_fee_p2sh()
+ self.test_fee_4of5()
+ self.test_spend_2of2()
+ self.test_locked_wallet()
+ self.test_many_inputs_fee()
+ self.test_many_inputs_send()
+ self.test_op_return()
+ self.test_watchonly()
+ self.test_all_watched_funds()
+ self.test_option_feerate()
+ self.test_address_reuse()
+ self.test_option_subtract_fee_from_outputs()
+
+ def test_change_position(self):
# ensure that setting changePosition in fundraw with an exact match is handled properly
rawmatch = self.nodes[2].createrawtransaction([], {self.nodes[2].getnewaddress():50})
rawmatch = self.nodes[2].fundrawtransaction(rawmatch, {"changePosition":1, "subtractFeeFromOutputs":[0]})
@@ -67,15 +97,15 @@ class RawTransactionsTest(BitcoinTestFramework):
watchonly_address = self.nodes[0].getnewaddress()
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
- watchonly_amount = Decimal(200)
+ self.watchonly_amount = Decimal(200)
self.nodes[3].importpubkey(watchonly_pubkey, "", True)
- watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount)
+ self.watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, self.watchonly_amount)
# Lock UTXO so nodes[0] doesn't accidentally spend it
- watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid, watchonly_address)
- self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
+ self.watchonly_vout = find_vout_for_address(self.nodes[0], self.watchonly_txid, watchonly_address)
+ self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
- self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10)
+ self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), self.watchonly_amount / 10)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
@@ -84,6 +114,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
+ def test_simple(self):
###############
# simple test #
###############
@@ -92,10 +123,10 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test that we have enough inputs
+ def test_simple_two_coins(self):
##############################
# simple test with two coins #
##############################
@@ -105,25 +136,11 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test if we have enough inputs
-
- ##############################
- # simple test with two coins #
- ##############################
- inputs = [ ]
- outputs = { self.nodes[0].getnewaddress() : 2.6 }
- rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
- rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
- dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
-
+ def test_simple_two_outputs(self):
################################
# simple test with two outputs #
################################
@@ -133,7 +150,6 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
for out in dec_tx['vout']:
@@ -142,7 +158,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
-
+ def test_change(self):
#########################################################################
# test a fundrawtransaction with a VIN greater than the required amount #
#########################################################################
@@ -156,6 +172,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
+ self.test_no_change_fee = fee # Use the same fee for the next tx
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
for out in dec_tx['vout']:
@@ -163,14 +180,14 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
-
+ def test_no_change(self):
#####################################################################
# test a fundrawtransaction with which will not get a change output #
#####################################################################
utx = get_unspent(self.nodes[2].listunspent(), 5)
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
- outputs = { self.nodes[0].getnewaddress() : Decimal(5.0) - fee - feeTolerance }
+ outputs = {self.nodes[0].getnewaddress(): Decimal(5.0) - self.test_no_change_fee - self.fee_tolerance}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
@@ -185,7 +202,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(rawtxfund['changepos'], -1)
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
-
+ def test_invalid_option(self):
####################################################
# test a fundrawtransaction with an invalid option #
####################################################
@@ -202,6 +219,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# reserveChangeKey was deprecated and is now removed
assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True}))
+ def test_invalid_change_address(self):
############################################################
# test a fundrawtransaction with an invalid change address #
############################################################
@@ -215,6 +233,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
+ def test_valid_change_address(self):
############################################################
# test a fundrawtransaction with a provided change address #
############################################################
@@ -233,6 +252,7 @@ class RawTransactionsTest(BitcoinTestFramework):
out = dec_tx['vout'][0]
assert_equal(change, out['scriptPubKey']['addresses'][0])
+ def test_change_type(self):
#########################################################
# test a fundrawtransaction with a provided change type #
#########################################################
@@ -247,6 +267,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])
+ def test_coin_selection(self):
#########################################################################
# test a fundrawtransaction with a VIN smaller than the required amount #
#########################################################################
@@ -264,7 +285,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -281,7 +301,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 1)
assert_equal(len(dec_tx['vout']), 2)
-
+ def test_two_vin(self):
###########################################
# test a fundrawtransaction with two VINs #
###########################################
@@ -295,7 +315,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -315,6 +334,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingIns, 2) #we now must see two vins identical to vins given as params
+ def test_two_vin_two_vout(self):
#########################################################
# test a fundrawtransaction with two VINs and two vOUTs #
#########################################################
@@ -328,7 +348,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -340,16 +359,16 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 2)
assert_equal(len(dec_tx['vout']), 3)
+ def test_invalid_input(self):
##############################################
# test a fundrawtransaction with invalid vin #
##############################################
inputs = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin!
outputs = { self.nodes[0].getnewaddress() : 1.0}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)
+ def test_fee_p2pkh(self):
############################################################
#compare fee of a standard pubkeyhash transaction
inputs = []
@@ -363,9 +382,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
+ def test_fee_p2pkh_multi_out(self):
############################################################
#compare fee of a standard pubkeyhash transaction with multiple outputs
inputs = []
@@ -378,10 +398,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_fee_p2sh(self):
############################################################
#compare fee of a 2of2 multisig p2sh transaction
@@ -405,10 +425,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_fee_4of5(self):
############################################################
#compare fee of a standard pubkeyhash transaction
@@ -438,10 +458,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_spend_2of2(self):
############################################################
# spend a 2of2 multisig transaction over fundraw
@@ -456,7 +476,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# send 1.2 BTC to msig addr
- txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
+ self.nodes[0].sendtoaddress(mSigObj, 1.2)
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
@@ -468,7 +488,7 @@ class RawTransactionsTest(BitcoinTestFramework):
fundedTx = self.nodes[2].fundrawtransaction(rawtx)
signedTx = self.nodes[2].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
+ self.nodes[2].sendrawtransaction(signedTx['hex'])
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
@@ -476,6 +496,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# make sure funds are received at node1
assert_equal(oldBalance+Decimal('1.10000000'), self.nodes[1].getbalance())
+ def test_locked_wallet(self):
############################################################
# locked wallet test
self.nodes[1].encryptwallet("test")
@@ -485,7 +506,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
- node.settxfee(min_relay_tx_fee)
+ node.settxfee(self.min_relay_tx_fee)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -493,7 +514,7 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes,0,3)
# Again lock the watchonly UTXO or nodes[0] may spend it, because
# lockunspent is memory-only and thus lost on restart
- self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
+ self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
self.sync_all()
# drain the keypool
@@ -523,14 +544,14 @@ class RawTransactionsTest(BitcoinTestFramework):
#now we need to unlock
self.nodes[1].walletpassphrase("test", 600)
signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
+ self.nodes[1].sendrawtransaction(signedTx['hex'])
self.nodes[1].generate(1)
self.sync_all()
# make sure funds are received at node1
assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance())
-
+ def test_many_inputs_fee(self):
###############################################
# multiple (~19) inputs tx test | Compare fee #
###############################################
@@ -558,9 +579,9 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance*19 #~19 inputs
-
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance * 19 #~19 inputs
+ def test_many_inputs_send(self):
#############################################
# multiple (~19) inputs tx test | sign/send #
#############################################
@@ -584,12 +605,13 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawtx)
fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
+ self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
assert_equal(oldBalance+Decimal('50.19000000'), self.nodes[0].getbalance()) #0.19+block reward
+ def test_op_return(self):
#####################################################
# test fundrawtransaction with OP_RETURN and no vin #
#####################################################
@@ -606,40 +628,41 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_greater_than(len(dec_tx['vin']), 0) # at least one vin
assert_equal(len(dec_tx['vout']), 2) # one change output added
-
+ def test_watchonly(self):
##################################################
# test a fundrawtransaction using only watchonly #
##################################################
inputs = []
- outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
+ outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount / 2}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
result = self.nodes[3].fundrawtransaction(rawtx, {'includeWatching': True })
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 1)
- assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)
+ assert_equal(res_dec["vin"][0]["txid"], self.watchonly_txid)
assert "fee" in result.keys()
assert_greater_than(result["changepos"], -1)
+ def test_all_watched_funds(self):
###############################################################
# test fundrawtransaction using the entirety of watched funds #
###############################################################
inputs = []
- outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
+ outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
# Backward compatibility test (2nd param is includeWatching)
result = self.nodes[3].fundrawtransaction(rawtx, True)
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 2)
- assert res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid
+ assert res_dec["vin"][0]["txid"] == self.watchonly_txid or res_dec["vin"][1]["txid"] == self.watchonly_txid
assert_greater_than(result["fee"], 0)
assert_greater_than(result["changepos"], -1)
- assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10)
+ assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], self.watchonly_amount / 10)
signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"])
assert not signedtx["complete"]
@@ -649,6 +672,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
+ def test_option_feerate(self):
#######################
# Test feeRate option #
#######################
@@ -659,17 +683,20 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {self.nodes[3].getnewaddress() : 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
- result = self.nodes[3].fundrawtransaction(rawtx) # uses min_relay_tx_fee (set by settxfee)
- result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee})
- result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10*min_relay_tx_fee})
+ result = self.nodes[3].fundrawtransaction(rawtx) # uses self.min_relay_tx_fee (set by settxfee)
+ result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
+ result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
+ assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
+ def test_address_reuse(self):
################################
# Test no address reuse occurs #
################################
+ rawtx = self.nodes[3].createrawtransaction(inputs=[], outputs={self.nodes[3].getnewaddress(): 1})
result3 = self.nodes[3].fundrawtransaction(rawtx)
res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
changeaddress = ""
@@ -681,6 +708,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# Now the change address key should be removed from the keypool
assert changeaddress != nextaddr
+ def test_option_subtract_fee_from_outputs(self):
######################################
# Test subtractFeeFromOutputs option #
######################################
@@ -692,11 +720,11 @@ class RawTransactionsTest(BitcoinTestFramework):
outputs = {self.nodes[2].getnewaddress(): 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
- result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee)
- self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
- self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses min_relay_tx_fee (set by settxfee)
- self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}),
- self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee, "subtractFeeFromOutputs": [0]})]
+ result = [self.nodes[3].fundrawtransaction(rawtx), # uses self.min_relay_tx_fee (set by settxfee)
+ self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
+ self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses self.min_relay_tx_fee (set by settxfee)
+ self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee}),
+ self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee, "subtractFeeFromOutputs": [0]}),]
dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result]
output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)]
diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py
index 3d3f694fd3..595b40f7cb 100755
--- a/test/functional/rpc_invalidateblock.py
+++ b/test/functional/rpc_invalidateblock.py
@@ -8,7 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.util import (
assert_equal,
- connect_nodes_bi,
+ connect_nodes,
wait_until,
)
@@ -33,7 +33,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getblockcount(), 6)
self.log.info("Connect nodes to force a reorg")
- connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes(self.nodes[0], 1)
self.sync_blocks(self.nodes[0:2])
assert_equal(self.nodes[0].getblockcount(), 6)
badhash = self.nodes[1].getblockhash(2)
@@ -44,7 +44,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbestblockhash(), besthash_n0)
self.log.info("Make sure we won't reorg to a lower work chain:")
- connect_nodes_bi(self.nodes, 1, 2)
+ connect_nodes(self.nodes[1], 2)
self.log.info("Sync node 2 to node 1 so both have 6 blocks")
self.sync_blocks(self.nodes[1:3])
assert_equal(self.nodes[2].getblockcount(), 6)
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index b12eb1d9ec..104c66aaa7 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -20,7 +20,32 @@ from test_framework.util import (
wait_until,
)
from test_framework.mininode import P2PInterface
-from test_framework.messages import CAddress, msg_addr, NODE_NETWORK, NODE_WITNESS
+from test_framework.messages import (
+ CAddress,
+ msg_addr,
+ NODE_NETWORK,
+ NODE_WITNESS,
+ NODE_GETUTXO,NODE_BLOOM,
+ NODE_NETWORK_LIMITED,
+)
+
+def assert_net_servicesnames(servicesflag, servicenames):
+ """Utility that checks if all flags are correctly decoded in
+ `getpeerinfo` and `getnetworkinfo`.
+
+ :param servicesflag: The services as an integer.
+ :param servicesnames: The list of decoded services names, as strings.
+ """
+ if servicesflag & NODE_NETWORK:
+ assert "NETWORK" in servicenames
+ if servicesflag & NODE_GETUTXO:
+ assert "GETUTXO" in servicenames
+ if servicesflag & NODE_BLOOM:
+ assert "BLOOM" in servicenames
+ if servicesflag & NODE_WITNESS:
+ assert "WITNESS" in servicenames
+ if servicesflag & NODE_NETWORK_LIMITED:
+ assert "NETWORK_LIMITED" in servicenames
class NetTest(BitcoinTestFramework):
def set_test_params(self):
@@ -31,7 +56,7 @@ class NetTest(BitcoinTestFramework):
def run_test(self):
self._test_connection_count()
self._test_getnettotals()
- self._test_getnetworkinginfo()
+ self._test_getnetworkinfo()
self._test_getaddednodeinfo()
self._test_getpeerinfo()
self._test_getnodeaddresses()
@@ -70,7 +95,7 @@ class NetTest(BitcoinTestFramework):
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
- def _test_getnetworkinginfo(self):
+ def _test_getnetworkinfo(self):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
@@ -84,6 +109,11 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ # check the `servicesnames` field
+ network_info = [node.getnetworkinfo() for node in self.nodes]
+ for info in network_info:
+ assert_net_servicesnames(int(info["localservices"]), info["localservicesnames"])
+
def _test_getaddednodeinfo(self):
assert_equal(self.nodes[0].getaddednodeinfo(), [])
# add a node (node2) to node0
@@ -104,6 +134,9 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr'])
assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500"))
assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000"))
+ # check the `servicesnames` field
+ for info in peer_info:
+ assert_net_servicesnames(int(info[0]["services"]), info[0]["servicesnames"])
def _test_getnodeaddresses(self):
self.nodes[0].add_p2p_connection(P2PInterface())
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 8a7ea7aa58..5a04e0c8d8 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -9,6 +9,7 @@ from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than,
assert_raises_rpc_error,
connect_nodes_bi,
disconnect_nodes,
@@ -26,6 +27,11 @@ class PSBTTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = False
self.num_nodes = 3
+ self.extra_args = [
+ ["-walletrbf=1"],
+ ["-walletrbf=0"],
+ []
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -129,6 +135,15 @@ class PSBTTest(BitcoinTestFramework):
assert_equal(walletprocesspsbt_out['complete'], True)
self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt(walletprocesspsbt_out['psbt'])['hex'])
+ # feeRate of 0.1 BTC / KB produces a total fee slightly below -maxtxfee (~0.05280000):
+ res = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 0.1})
+ assert_greater_than(res["fee"], 0.05)
+ assert_greater_than(0.06, res["fee"])
+
+ # feeRate of 10 BTC / KB produces a total fee well above -maxtxfee
+ # previously this was silently capped at -maxtxfee
+ assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 10})
+
# partially sign multisig things with node 1
psbtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], {self.nodes[1].getnewaddress():29.99})['psbt']
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx)
@@ -152,9 +167,10 @@ class PSBTTest(BitcoinTestFramework):
# Make sure that a non-psbt with signatures cannot be converted
# Error could be either "TX decode failed" (segwit inputs causes parsing to fail) or "Inputs must not have scriptSigs and scriptWitnesses"
+ # We must set iswitness=True because the serialized transaction has inputs and is therefore a witness transaction
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])
- assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, signedtx['hex'])
- assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, signedtx['hex'], False)
+ assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], iswitness=True)
+ assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], permitsigdata=False, iswitness=True)
# Unless we allow it to convert and strip signatures
self.nodes[0].converttopsbt(signedtx['hex'], True)
@@ -196,18 +212,18 @@ class PSBTTest(BitcoinTestFramework):
# replaceable arg
block_height = self.nodes[0].getblockcount()
unspent = self.nodes[0].listunspent()[0]
- psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False)
+ psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False}, False)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert "bip32_derivs" not in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
- # Same construction with only locktime set
- psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {}, True)
+ # Same construction with only locktime set and RBF explicitly enabled
+ psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True}, True)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert "bip32_derivs" in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height)
@@ -215,9 +231,16 @@ class PSBTTest(BitcoinTestFramework):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in in decoded_psbt["tx"]["vin"]:
- assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert_equal(decoded_psbt["tx"]["locktime"], 0)
+ # Same construction without optional arguments, for a node with -walletrbf=0
+ unspent1 = self.nodes[1].listunspent()[0]
+ psbtx_info = self.nodes[1].walletcreatefundedpsbt([{"txid":unspent1["txid"], "vout":unspent1["vout"]}], [{self.nodes[2].getnewaddress():unspent1["amount"]+1}], block_height)
+ decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"])
+ for tx_in in decoded_psbt["tx"]["vin"]:
+ assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+
# Make sure change address wallet does not have P2SH innerscript access to results in success
# when attempting BnB coin selection
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
@@ -314,18 +337,32 @@ class PSBTTest(BitcoinTestFramework):
vout3 = find_output(self.nodes[0], txid3, 11)
self.sync_all()
- # Update a PSBT with UTXOs from the node
- # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
+ def test_psbt_input_keys(psbt_input, keys):
+ """Check that the psbt input has only the expected keys."""
+ assert_equal(set(keys), set(psbt_input.keys()))
+
+ # Create a PSBT. None of the inputs are filled initially
psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
decoded = self.nodes[1].decodepsbt(psbt)
- assert "witness_utxo" not in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
- assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
- assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+ test_psbt_input_keys(decoded['inputs'][0], [])
+ test_psbt_input_keys(decoded['inputs'][1], [])
+ test_psbt_input_keys(decoded['inputs'][2], [])
+
+ # Update a PSBT with UTXOs from the node
+ # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
updated = self.nodes[1].utxoupdatepsbt(psbt)
decoded = self.nodes[1].decodepsbt(updated)
- assert "witness_utxo" in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
- assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
- assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+ test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo'])
+ test_psbt_input_keys(decoded['inputs'][1], [])
+ test_psbt_input_keys(decoded['inputs'][2], [])
+
+ # Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in
+ descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]
+ updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)
+ decoded = self.nodes[1].decodepsbt(updated)
+ test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'bip32_derivs'])
+ test_psbt_input_keys(decoded['inputs'][1], [])
+ test_psbt_input_keys(decoded['inputs'][2], ['witness_utxo', 'bip32_derivs', 'redeem_script'])
# Two PSBTs with a common input should not be joinable
psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index a1cd33ad54..9f94d11a93 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -58,6 +58,13 @@ class ScantxoutsetTest(BitcoinTestFramework):
self.start_node(0)
self.nodes[0].generate(110)
+ scan = self.nodes[0].scantxoutset("start", [])
+ info = self.nodes[0].gettxoutsetinfo()
+ assert_equal(scan['success'], True)
+ assert_equal(scan['height'], info['height'])
+ assert_equal(scan['txouts'], info['txouts'])
+ assert_equal(scan['bestblock'], info['bestblock'])
+
self.restart_node(0, ['-nowallet'])
self.log.info("Test if we have found the non HD unspent outputs.")
assert_equal(self.nodes[0].scantxoutset("start", [ "pkh(" + pubk1 + ")", "pkh(" + pubk2 + ")", "pkh(" + pubk3 + ")"])['total_amount'], Decimal("0.002"))
diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py
new file mode 100755
index 0000000000..b1d2b6f431
--- /dev/null
+++ b/test/functional/rpc_setban.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2019 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 the setban rpc call."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ connect_nodes,
+ p2p_port
+)
+
+class SetBanTests(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [[],[]]
+
+ def run_test(self):
+ # Node 0 connects to Node 1, check that the noban permission is not granted
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert(not 'noban' in peerinfo['permissions'])
+
+ # Node 0 get banned by Node 1
+ self.nodes[1].setban("127.0.0.1", "add")
+
+ # Node 0 should not be able to reconnect
+ with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n'], timeout=5):
+ self.restart_node(1, [])
+ self.nodes[0].addnode("127.0.0.1:" + str(p2p_port(1)), "onetry")
+
+ # However, node 0 should be able to reconnect if it has noban permission
+ self.restart_node(1, ['-whitelist=127.0.0.1'])
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert('noban' in peerinfo['permissions'])
+
+ # If we remove the ban, Node 0 should be able to reconnect even without noban permission
+ self.nodes[1].setban("127.0.0.1", "remove")
+ self.restart_node(1, [])
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert(not 'noban' in peerinfo['permissions'])
+
+if __name__ == '__main__':
+ SetBanTests().main()
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 102dd22594..8bbb3c04fa 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -20,6 +20,17 @@ import string
import configparser
import sys
+def call_with_auth(node, user, password):
+ url = urllib.parse.urlparse(node.url)
+ headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user, password))}
+
+ conn = http.client.HTTPConnection(url.hostname, url.port)
+ conn.connect()
+ conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+ resp = conn.getresponse()
+ conn.close()
+ return resp
+
class HTTPBasicsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -28,15 +39,24 @@ class HTTPBasicsTest(BitcoinTestFramework):
def setup_chain(self):
super().setup_chain()
#Append rpcauth to bitcoin.conf before initialization
+ self.rtpassword = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144"
- rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
- rpcuser = "rpcuser=rpcuser💻"
- rpcpassword = "rpcpassword=rpcpassword🔑"
- self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
+ self.rpcuser = "rpcuser💻"
+ self.rpcpassword = "rpcpassword🔑"
+
config = configparser.ConfigParser()
config.read_file(open(self.options.configfile))
gen_rpcauth = config['environment']['RPCAUTH']
+
+ # Generate RPCAUTH with specified password
+ self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
+ p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, universal_newlines=True)
+ lines = p.stdout.read().splitlines()
+ rpcauth2 = lines[1]
+
+ # Generate RPCAUTH without specifying password
+ self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True)
lines = p.stdout.read().splitlines()
rpcauth3 = lines[1]
@@ -47,160 +67,40 @@ class HTTPBasicsTest(BitcoinTestFramework):
f.write(rpcauth2+"\n")
f.write(rpcauth3+"\n")
with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), 'a', encoding='utf8') as f:
- f.write(rpcuser+"\n")
- f.write(rpcpassword+"\n")
-
- def run_test(self):
-
- ##################################################
- # Check correctness of the rpcauth config option #
- ##################################################
- url = urllib.parse.urlparse(self.nodes[0].url)
-
- #Old authpair
- authpair = url.username + ':' + url.password
-
- #New authpair generated via share/rpcauth tool
- password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
-
- #Second authpair with different username
- password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
- authpairnew = "rt:"+password
+ f.write("rpcuser={}\n".format(self.rpcuser))
+ f.write("rpcpassword={}\n".format(self.rpcpassword))
+ def test_auth(self, node, user, password):
self.log.info('Correct...')
- headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
+ assert_equal(200, call_with_auth(node, user, password).status)
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Use new authpair to confirm both work
- self.log.info('Correct...')
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Wrong login name with rt's password
self.log.info('Wrong...')
- authpairnew = "rtwrong:"+password
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
+ assert_equal(401, call_with_auth(node, user, password+'wrong').status)
- #Wrong password for rt
self.log.info('Wrong...')
- authpairnew = "rt:"+password+"wrong"
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+ assert_equal(401, call_with_auth(node, user+'wrong', password).status)
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
-
- #Correct for rt2
- self.log.info('Correct...')
- authpairnew = "rt2:"+password2
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Wrong password for rt2
self.log.info('Wrong...')
- authpairnew = "rt2:"+password2+"wrong"
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
+ assert_equal(401, call_with_auth(node, user+'wrong', password+'wrong').status)
- #Correct for randomly generated user
- self.log.info('Correct...')
- authpairnew = self.user+":"+self.password
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
+ def run_test(self):
- #Wrong password for randomly generated user
- self.log.info('Wrong...')
- authpairnew = self.user+":"+self.password+"Wrong"
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+ ##################################################
+ # Check correctness of the rpcauth config option #
+ ##################################################
+ url = urllib.parse.urlparse(self.nodes[0].url)
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
+ self.test_auth(self.nodes[0], url.username, url.password)
+ self.test_auth(self.nodes[0], 'rt', self.rtpassword)
+ self.test_auth(self.nodes[0], 'rt2', self.rt2password)
+ self.test_auth(self.nodes[0], self.user, self.password)
###############################################################
# Check correctness of the rpcuser/rpcpassword config options #
###############################################################
url = urllib.parse.urlparse(self.nodes[1].url)
- # rpcuser and rpcpassword authpair
- self.log.info('Correct...')
- rpcuserauthpair = "rpcuser💻:rpcpassword🔑"
-
- headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Wrong login name with rpcuser's password
- rpcuserauthpair = "rpcuserwrong:rpcpassword"
- headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
-
- #Wrong password for rpcuser
- self.log.info('Wrong...')
- rpcuserauthpair = "rpcuser:rpcpasswordwrong"
- headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
-
+ self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword)
if __name__ == '__main__':
HTTPBasicsTest ().main ()
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index f36cffe957..194f2f061b 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
+import enum
+
from .script import hash256, hash160, sha256, CScript, OP_0
from .util import hex_str_to_bytes
@@ -11,6 +13,13 @@ from . import segwit_addr
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
+
+class AddressType(enum.Enum):
+ bech32 = 'bech32'
+ p2sh_segwit = 'p2sh-segwit'
+ legacy = 'legacy' # P2PKH
+
+
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 7ac044d0d0..d741b00ba0 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -22,13 +22,14 @@ from .messages import (
ToHex,
hash256,
hex_str_to_bytes,
- ser_string,
ser_uint256,
sha256,
uint256_from_str,
)
from .script import (
CScript,
+ CScriptNum,
+ CScriptOp,
OP_0,
OP_1,
OP_CHECKMULTISIG,
@@ -89,20 +90,14 @@ def add_witness_commitment(block, nonce=0):
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
-def serialize_script_num(value):
- r = bytearray(0)
- if value == 0:
- return r
- neg = value < 0
- absvalue = -value if neg else value
- while (absvalue):
- r.append(int(absvalue & 0xff))
- absvalue >>= 8
- if r[-1] & 0x80:
- r.append(0x80 if neg else 0)
- elif neg:
- r[-1] |= 0x80
- return r
+
+def script_BIP34_coinbase_height(height):
+ if height <= 16:
+ res = CScriptOp.encode_op_n(height)
+ # Append dummy to increase scriptSig size above 2 (see bad-cb-length consensus rule)
+ return CScript([res, OP_1])
+ return CScript([CScriptNum(height)])
+
def create_coinbase(height, pubkey=None):
"""Create a coinbase transaction, assuming no miner fees.
@@ -110,8 +105,7 @@ def create_coinbase(height, pubkey=None):
If pubkey is passed in, the coinbase output will be a P2PK output;
otherwise an anyone-can-spend output."""
coinbase = CTransaction()
- coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff),
- ser_string(serialize_script_num(height)), 0xffffffff))
+ coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), script_BIP34_coinbase_height(height), 0xffffffff))
coinbaseoutput = CTxOut()
coinbaseoutput.nValue = 50 * COIN
halvings = int(height / 150) # regtest
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 954ae3c4df..3cb5f56bee 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -43,7 +43,7 @@ COIN = 100000000 # 1 btc in satoshis
BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
NODE_NETWORK = (1 << 0)
-# NODE_GETUTXO = (1 << 1)
+NODE_GETUTXO = (1 << 1)
NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
NODE_NETWORK_LIMITED = (1 << 10)
@@ -803,7 +803,9 @@ class HeaderAndShortIDs:
return [ key0, key1 ]
# Version 2 compact blocks use wtxid in shortids (rather than txid)
- def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
+ def initialize_from_block(self, block, nonce=0, prefill_list=None, use_witness=False):
+ if prefill_list is None:
+ prefill_list = [0]
self.header = CBlockHeader(block)
self.nonce = nonce
self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
@@ -1130,7 +1132,7 @@ class msg_block:
self.block.deserialize(f)
def serialize(self):
- return self.block.serialize(with_witness=False)
+ return self.block.serialize()
def __repr__(self):
return "msg_block(block=%s)" % (repr(self.block))
@@ -1152,11 +1154,10 @@ class msg_generic:
return "msg_generic()"
-class msg_witness_block(msg_block):
+class msg_no_witness_block(msg_block):
__slots__ = ()
def serialize(self):
- r = self.block.serialize(with_witness=True)
- return r
+ return self.block.serialize(with_witness=False)
class msg_getaddr:
@@ -1442,17 +1443,15 @@ class msg_blocktxn:
def serialize(self):
r = b""
- r += self.block_transactions.serialize(with_witness=False)
+ r += self.block_transactions.serialize()
return r
def __repr__(self):
return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
-class msg_witness_blocktxn(msg_blocktxn):
+class msg_no_witness_blocktxn(msg_blocktxn):
__slots__ = ()
def serialize(self):
- r = b""
- r += self.block_transactions.serialize(with_witness=True)
- return r
+ return self.block_transactions.serialize(with_witness=False)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index cc3a4cc72a..166438cf70 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -111,7 +111,7 @@ class P2PConnection(asyncio.Protocol):
def is_connected(self):
return self._transport is not None
- def peer_connect(self, dstaddr, dstport, net="regtest"):
+ def peer_connect(self, dstaddr, dstport, *, net):
assert not self.is_connected
self.dstaddr = dstaddr
self.dstport = dstport
@@ -363,6 +363,7 @@ class P2PInterface(P2PConnection):
def wait_for_tx(self, txid, timeout=60):
def test_function():
+ assert self.is_connected
if not self.last_message.get('tx'):
return False
return self.last_message['tx'].tx.rehash() == txid
@@ -370,11 +371,15 @@ class P2PInterface(P2PConnection):
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_block(self, blockhash, timeout=60):
- test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_header(self, blockhash, timeout=60):
def test_function():
+ assert self.is_connected
last_headers = self.last_message.get('headers')
if not last_headers:
return False
@@ -389,7 +394,11 @@ class P2PInterface(P2PConnection):
value must be explicitly cleared before calling this method, or this will return
immediately with success. TODO: change this method to take a hash value and only
return true if the correct block/tx has been requested."""
- test_function = lambda: self.last_message.get("getdata")
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("getdata")
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_getheaders(self, timeout=60):
@@ -399,20 +408,30 @@ class P2PInterface(P2PConnection):
value must be explicitly cleared before calling this method, or this will return
immediately with success. TODO: change this method to take a hash value and only
return true if the correct block header has been requested."""
- test_function = lambda: self.last_message.get("getheaders")
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("getheaders")
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_inv(self, expected_inv, timeout=60):
"""Waits for an INV message and checks that the first inv object in the message was as expected."""
if len(expected_inv) > 1:
raise NotImplementedError("wait_for_inv() will only verify the first inv object")
- test_function = lambda: self.last_message.get("inv") and \
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("inv") and \
self.last_message["inv"].inv[0].type == expected_inv[0].type and \
self.last_message["inv"].inv[0].hash == expected_inv[0].hash
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_verack(self, timeout=60):
- test_function = lambda: self.message_count["verack"]
+ def test_function():
+ return self.message_count["verack"]
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
# Message sending helper functions
@@ -424,7 +443,11 @@ class P2PInterface(P2PConnection):
# Sync up with the node
def sync_with_ping(self, timeout=60):
self.send_message(msg_ping(nonce=self.ping_counter))
- test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
self.ping_counter += 1
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index aa674f9ebe..9aff08fdc7 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -91,6 +91,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def __init__(self):
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
+ self.chain = 'regtest'
self.setup_clean_chain = False
self.nodes = []
self.network_thread = None
@@ -192,18 +193,18 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.setup_network()
self.run_test()
success = TestStatus.PASSED
- except JSONRPCException as e:
+ except JSONRPCException:
self.log.exception("JSONRPC error")
except SkipTest as e:
self.log.warning("Test Skipped: %s" % e.message)
success = TestStatus.SKIPPED
- except AssertionError as e:
+ except AssertionError:
self.log.exception("Assertion failed")
- except KeyError as e:
+ except KeyError:
self.log.exception("Key error")
- except Exception as e:
+ except Exception:
self.log.exception("Unexpected exception caught during testing")
- except KeyboardInterrupt as e:
+ except KeyboardInterrupt:
self.log.warning("Exiting after keyboard interrupt")
if success == TestStatus.FAILED and self.options.pdbonfailure:
@@ -342,6 +343,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes.append(TestNode(
i,
get_datadir_path(self.options.tmpdir, i),
+ chain=self.chain,
rpchost=rpchost,
timewait=self.rpc_timeout,
bitcoind=binary[i],
@@ -477,11 +479,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if not os.path.isdir(cache_node_dir):
self.log.debug("Creating cache directory {}".format(cache_node_dir))
- initialize_datadir(self.options.cachedir, CACHE_NODE_ID)
+ initialize_datadir(self.options.cachedir, CACHE_NODE_ID, self.chain)
self.nodes.append(
TestNode(
CACHE_NODE_ID,
cache_node_dir,
+ chain=self.chain,
extra_conf=["bind=127.0.0.1"],
extra_args=['-disablewallet'],
rpchost=None,
@@ -515,7 +518,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes = []
def cache_path(*paths):
- return os.path.join(cache_node_dir, "regtest", *paths)
+ return os.path.join(cache_node_dir, self.chain, *paths)
os.rmdir(cache_path('wallets')) # Remove empty wallets dir
for entry in os.listdir(cache_path()):
@@ -526,7 +529,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.log.debug("Copy cache directory {} to node {}".format(cache_node_dir, i))
to_dir = get_datadir_path(self.options.tmpdir, i)
shutil.copytree(cache_node_dir, to_dir)
- initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf
+ initialize_datadir(self.options.tmpdir, i, self.chain) # Overwrite port/rpcport in bitcoin.conf
def _initialize_chain_clean(self):
"""Initialize empty blockchain for use by the test.
@@ -534,7 +537,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
Create an empty blockchain and num_nodes wallets.
Useful if a test case wants complete control over initialization."""
for i in range(self.num_nodes):
- initialize_datadir(self.options.tmpdir, i)
+ initialize_datadir(self.options.tmpdir, i, self.chain)
def skip_if_no_py3_zmq(self):
"""Attempt to import the zmq package and skip the test if the import fails."""
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 3311377090..d8bfdfd040 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -59,7 +59,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir, *, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
+ def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
"""
Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -68,8 +68,10 @@ class TestNode():
self.index = i
self.datadir = datadir
+ self.bitcoinconf = os.path.join(self.datadir, "bitcoin.conf")
self.stdout_dir = os.path.join(self.datadir, "stdout")
self.stderr_dir = os.path.join(self.datadir, "stderr")
+ self.chain = chain
self.rpchost = rpchost
self.rpc_timeout = timewait
self.binary = bitcoind
@@ -197,7 +199,7 @@ class TestNode():
# Delete any existing cookie file -- if such a file exists (eg due to
# unclean shutdown), it will get overwritten anyway by bitcoind, and
# potentially interfere with our attempt to authenticate
- delete_cookie_file(self.datadir)
+ delete_cookie_file(self.datadir, self.chain)
# add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal
subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1")
@@ -219,7 +221,7 @@ class TestNode():
raise FailedToStartError(self._node_msg(
'bitcoind exited with status {} during initialization'.format(self.process.returncode)))
try:
- rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
+ rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.chain, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
rpc.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
self.log.debug("RPC successfully started")
@@ -305,21 +307,30 @@ class TestNode():
wait_until(self.is_node_stopped, timeout=timeout)
@contextlib.contextmanager
- def assert_debug_log(self, expected_msgs):
- debug_log = os.path.join(self.datadir, 'regtest', 'debug.log')
+ def assert_debug_log(self, expected_msgs, timeout=2):
+ time_end = time.time() + timeout
+ debug_log = os.path.join(self.datadir, self.chain, 'debug.log')
with open(debug_log, encoding='utf-8') as dl:
dl.seek(0, 2)
prev_size = dl.tell()
- try:
- yield
- finally:
+
+ yield
+
+ while True:
+ found = True
with open(debug_log, encoding='utf-8') as dl:
dl.seek(prev_size)
log = dl.read()
print_log = " - " + "\n - ".join(log.splitlines())
for expected_msg in expected_msgs:
if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None:
- self._raise_assertion_error('Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log))
+ found = False
+ if found:
+ return
+ if time.time() >= time_end:
+ break
+ time.sleep(0.05)
+ self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
def assert_memory_usage_stable(self, *, increase_allowed=0.03):
@@ -478,7 +489,7 @@ class TestNode():
if 'dstaddr' not in kwargs:
kwargs['dstaddr'] = '127.0.0.1'
- p2p_conn.peer_connect(**kwargs)()
+ p2p_conn.peer_connect(**kwargs, net=self.chain)()
self.p2ps.append(p2p_conn)
if wait_for_verack:
p2p_conn.wait_for_verack()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 26215083fb..f9f5fe553e 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -7,7 +7,6 @@
from base64 import b64encode
from binascii import unhexlify
from decimal import Decimal, ROUND_DOWN
-import hashlib
import inspect
import json
import logging
@@ -19,6 +18,7 @@ import time
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
+from io import BytesIO
logger = logging.getLogger("TestFramework.utils")
@@ -56,7 +56,9 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
except exc as e:
if message is not None and message not in e.error['message']:
- raise AssertionError("Expected substring not found:" + e.error['message'])
+ raise AssertionError(
+ "Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
+ message, e.error['message']))
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
else:
@@ -116,7 +118,9 @@ def try_rpc(code, message, fun, *args, **kwds):
if (code is not None) and (code != e.error["code"]):
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
if (message is not None) and (message not in e.error['message']):
- raise AssertionError("Expected substring not found:" + e.error['message'])
+ raise AssertionError(
+ "Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
+ message, e.error['message']))
return True
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
@@ -182,12 +186,6 @@ def check_json_precision():
def count_bytes(hex_string):
return len(bytearray.fromhex(hex_string))
-def hash256(byte_str):
- sha256 = hashlib.sha256()
- sha256.update(byte_str)
- sha256d = hashlib.sha256()
- sha256d.update(sha256.digest())
- return sha256d.digest()[::-1]
def hex_str_to_bytes(hex_str):
return unhexlify(hex_str.encode('ascii'))
@@ -270,8 +268,8 @@ def p2p_port(n):
def rpc_port(n):
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
-def rpc_url(datadir, i, rpchost=None):
- rpc_u, rpc_p = get_auth_cookie(datadir)
+def rpc_url(datadir, i, chain, rpchost):
+ rpc_u, rpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
port = rpc_port(i)
if rpchost:
@@ -285,20 +283,29 @@ def rpc_url(datadir, i, rpchost=None):
# Node functions
################
-def initialize_datadir(dirname, n):
+def initialize_datadir(dirname, n, chain):
datadir = get_datadir_path(dirname, n)
if not os.path.isdir(datadir):
os.makedirs(datadir)
+ # Translate chain name to config name
+ if chain == 'testnet3':
+ chain_name_conf_arg = 'testnet'
+ chain_name_conf_section = 'test'
+ else:
+ chain_name_conf_arg = chain
+ chain_name_conf_section = chain
with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
- f.write("regtest=1\n")
- f.write("[regtest]\n")
+ f.write("{}=1\n".format(chain_name_conf_arg))
+ f.write("[{}]\n".format(chain_name_conf_section))
f.write("port=" + str(p2p_port(n)) + "\n")
f.write("rpcport=" + str(rpc_port(n)) + "\n")
f.write("server=1\n")
f.write("keypool=1\n")
f.write("discover=0\n")
+ f.write("dnsseed=0\n")
f.write("listenonion=0\n")
f.write("printtoconsole=0\n")
+ f.write("upnp=0\n")
os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
return datadir
@@ -311,7 +318,7 @@ def append_config(datadir, options):
for option in options:
f.write(option + "\n")
-def get_auth_cookie(datadir):
+def get_auth_cookie(datadir, chain):
user = None
password = None
if os.path.isfile(os.path.join(datadir, "bitcoin.conf")):
@@ -324,7 +331,7 @@ def get_auth_cookie(datadir):
assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
try:
- with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f:
+ with open(os.path.join(datadir, chain, ".cookie"), 'r', encoding="ascii") as f:
userpass = f.read()
split_userpass = userpass.split(':')
user = split_userpass[0]
@@ -336,14 +343,14 @@ def get_auth_cookie(datadir):
return user, password
# If a cookie file exists in the given datadir, delete it.
-def delete_cookie_file(datadir):
- if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
+def delete_cookie_file(datadir, chain):
+ if os.path.isfile(os.path.join(datadir, chain, ".cookie")):
logger.debug("Deleting leftover cookie file")
- os.remove(os.path.join(datadir, "regtest", ".cookie"))
+ os.remove(os.path.join(datadir, chain, ".cookie"))
-def get_bip9_status(node, key):
- info = node.getblockchaininfo()
- return info['bip9_softforks'][key]
+def softfork_active(node, key):
+ """Return whether a softfork is active."""
+ return node.getblockchaininfo()['softforks'][key]['active']
def set_node_times(nodes, t):
for node in nodes:
@@ -515,14 +522,13 @@ def gen_return_txouts():
for i in range(512):
script_pubkey = script_pubkey + "01"
# concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
- txouts = "81"
+ txouts = []
+ from .messages import CTxOut
+ txout = CTxOut()
+ txout.nValue = 0
+ txout.scriptPubKey = hex_str_to_bytes(script_pubkey)
for k in range(128):
- # add txout value
- txouts = txouts + "0000000000000000"
- # add length of script_pubkey
- txouts = txouts + "fd0402"
- # add script_pubkey
- txouts = txouts + script_pubkey
+ txouts.append(txout)
return txouts
# Create a spend of each passed-in utxo, splicing in "txouts" to each raw
@@ -530,6 +536,7 @@ def gen_return_txouts():
def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
addr = node.getnewaddress()
txids = []
+ from .messages import CTransaction
for _ in range(num):
t = utxos.pop()
inputs = [{"txid": t["txid"], "vout": t["vout"]}]
@@ -537,9 +544,11 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
change = t['amount'] - fee
outputs[addr] = satoshi_round(change)
rawtx = node.createrawtransaction(inputs, outputs)
- newtx = rawtx[0:92]
- newtx = newtx + txouts
- newtx = newtx + rawtx[94:]
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex_str_to_bytes(rawtx)))
+ for txout in txouts:
+ tx.vout.append(txout)
+ newtx = tx.serialize().hex()
signresult = node.signrawtransactionwithwallet(newtx, None, "NONE")
txid = node.sendrawtransaction(signresult["hex"], 0)
txids.append(txid)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 80dced733e..9c92091f1d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -67,16 +67,15 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
EXTENDED_SCRIPTS = [
- # These tests are not run by the travis build process.
+ # These tests are not run by default.
# Longest test should go first, to favor running tests in parallel
'feature_pruning.py',
'feature_dbcrash.py',
]
BASE_SCRIPTS = [
- # Scripts that are run by the travis build process.
+ # Scripts that are run by default.
# Longest test should go first, to favor running tests in parallel
- 'feature_fee_estimation.py',
'wallet_hd.py',
'wallet_backup.py',
# vv Tests less than 5m vv
@@ -91,6 +90,7 @@ BASE_SCRIPTS = [
'wallet_labels.py',
'p2p_segwit.py',
'p2p_timeouts.py',
+ 'p2p_tx_download.py',
'wallet_dump.py',
'wallet_listtransactions.py',
# vv Tests less than 60s vv
@@ -107,8 +107,10 @@ BASE_SCRIPTS = [
'feature_bip68_sequence.py',
'p2p_feefilter.py',
'feature_reindex.py',
+ 'feature_abortnode.py',
# vv Tests less than 30s vv
'wallet_keypool_topup.py',
+ 'feature_fee_estimation.py',
'interface_zmq.py',
'interface_bitcoin_cli.py',
'mempool_resurrect.py',
@@ -120,12 +122,16 @@ BASE_SCRIPTS = [
'rpc_misc.py',
'interface_rest.py',
'mempool_spend_coinbase.py',
+ 'wallet_avoidreuse.py',
'mempool_reorg.py',
'mempool_persist.py',
'wallet_multiwallet.py',
'wallet_multiwallet.py --usecli',
'wallet_createwallet.py',
'wallet_createwallet.py --usecli',
+ 'wallet_watchonly.py',
+ 'wallet_watchonly.py --usecli',
+ 'wallet_reorgsrestore.py',
'interface_http.py',
'interface_rpc.py',
'rpc_psbt.py',
@@ -141,6 +147,7 @@ BASE_SCRIPTS = [
'rpc_net.py',
'wallet_keypool.py',
'p2p_mempool.py',
+ 'rpc_setban.py',
'p2p_blocksonly.py',
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
@@ -156,6 +163,7 @@ BASE_SCRIPTS = [
'rpc_invalidateblock.py',
'feature_rbf.py',
'mempool_packages.py',
+ 'mempool_package_onemore.py',
'rpc_createmultisig.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
@@ -172,6 +180,7 @@ BASE_SCRIPTS = [
'rpc_bind.py --nonloopback',
'mining_basic.py',
'wallet_bumpfee.py',
+ 'wallet_bumpfee_totalfee_deprecation.py',
'rpc_named_arguments.py',
'wallet_listsinceblock.py',
'p2p_leak.py',
@@ -188,6 +197,7 @@ BASE_SCRIPTS = [
'feature_uacomment.py',
'wallet_coinbase_category.py',
'feature_filelock.py',
+ 'p2p_dos_header_tree.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
'rpc_deriveaddresses.py',
@@ -195,6 +205,7 @@ BASE_SCRIPTS = [
'rpc_scantxoutset.py',
'feature_logging.py',
'p2p_node_network_limited.py',
+ 'p2p_permissions.py',
'feature_blocksdir.py',
'feature_config_args.py',
'rpc_help.py',
@@ -222,6 +233,7 @@ def main():
epilog='''
Help text and arguments for individual test script:''',
formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument('--ansi', action='store_true', default=sys.stdout.isatty(), help="Use ANSI colors and dots in output (enabled by default when standard output is a TTY)")
parser.add_argument('--combinedlogslen', '-c', type=int, default=0, metavar='n', help='On failure, print a log (of length n lines) to the console, combined from the test framework and all test nodes.')
parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface')
parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment')
@@ -233,7 +245,15 @@ def main():
parser.add_argument('--quiet', '-q', action='store_true', help='only print dots, results summary and failure logs')
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
+ parser.add_argument('--filter', help='filter scripts to run by regular expression')
+
args, unknown_args = parser.parse_known_args()
+ if not args.ansi:
+ global BOLD, GREEN, RED, GREY
+ BOLD = ("", "")
+ GREEN = ("", "")
+ RED = ("", "")
+ GREY = ("", "")
# args to be passed on always start with two dashes; tests are the remaining unknown args
tests = [arg for arg in unknown_args if arg[:2] != "--"]
@@ -268,11 +288,22 @@ def main():
test_list = []
if tests:
# Individual tests have been specified. Run specified tests that exist
- # in the ALL_SCRIPTS list. Accept the name with or without .py extension.
- tests = [test + ".py" if ".py" not in test else test for test in tests]
+ # in the ALL_SCRIPTS list. Accept names with or without a .py extension.
+ # Specified tests can contain wildcards, but in that case the supplied
+ # paths should be coherent, e.g. the same path as that provided to call
+ # test_runner.py. Examples:
+ # `test/functional/test_runner.py test/functional/wallet*`
+ # `test/functional/test_runner.py ./test/functional/wallet*`
+ # `test_runner.py wallet*`
+ # but not:
+ # `test/functional/test_runner.py wallet*`
+ # Multiple wildcards can be passed:
+ # `test_runner.py tool* mempool*`
for test in tests:
- if test in ALL_SCRIPTS:
- test_list.append(test)
+ script = test.split("/")[-1]
+ script = script + ".py" if ".py" not in script else script
+ if script in ALL_SCRIPTS:
+ test_list.append(script)
else:
print("{}WARNING!{} Test '{}' not found in full test list.".format(BOLD[1], BOLD[0], test))
elif args.extended:
@@ -293,6 +324,9 @@ def main():
if not exclude_list:
print("{}WARNING!{} Test '{}' not found in current test list.".format(BOLD[1], BOLD[0], exclude_test))
+ if args.filter:
+ test_list = list(filter(re.compile(args.filter).search, test_list))
+
if not test_list:
print("No valid test scripts specified. Check that your test is in one "
"of the test lists in test_runner.py, or run test_runner.py with no arguments to run all tests")
@@ -321,9 +355,10 @@ def main():
combined_logs_len=args.combinedlogslen,
failfast=args.failfast,
runs_ci=args.ci,
+ use_term_control=args.ansi,
)
-def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci):
+def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci, use_term_control):
args = args or []
# Warn if bitcoind is already running (unix only)
@@ -365,6 +400,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
test_list=test_list,
flags=flags,
timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds
+ use_term_control=use_term_control,
)
start_time = time.time()
test_results = []
@@ -448,7 +484,7 @@ class TestHandler:
Trigger the test scripts passed in via the list.
"""
- def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration):
+ def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration, use_term_control):
assert num_tests_parallel >= 1
self.num_jobs = num_tests_parallel
self.tests_dir = tests_dir
@@ -458,6 +494,7 @@ class TestHandler:
self.flags = flags
self.num_running = 0
self.jobs = []
+ self.use_term_control = use_term_control
def get_next(self):
while self.num_running < self.num_jobs and self.test_list:
@@ -494,7 +531,8 @@ class TestHandler:
for job in self.jobs:
(name, start_time, proc, testdir, log_out, log_err) = job
if int(time.time() - start_time) > self.timeout_duration:
- # In travis, timeout individual tests (to stop tests hanging and not providing useful output).
+ # Timeout individual tests if timeout is specified (to stop
+ # tests hanging and not providing useful output).
proc.send_signal(signal.SIGINT)
if proc.poll() is not None:
log_out.seek(0), log_err.seek(0)
@@ -508,11 +546,13 @@ class TestHandler:
status = "Failed"
self.num_running -= 1
self.jobs.remove(job)
- clearline = '\r' + (' ' * dot_count) + '\r'
- print(clearline, end='', flush=True)
+ if self.use_term_control:
+ clearline = '\r' + (' ' * dot_count) + '\r'
+ print(clearline, end='', flush=True)
dot_count = 0
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
- print('.', end='', flush=True)
+ if self.use_term_control:
+ print('.', end='', flush=True)
dot_count += 1
def kill_and_join(self):
@@ -582,7 +622,7 @@ def check_script_list(*, src_dir, fail_on_warn):
if len(missed_tests) != 0:
print("%sWARNING!%s The following scripts are not being run: %s. Check the test lists in test_runner.py." % (BOLD[1], BOLD[0], str(missed_tests)))
if fail_on_warn:
- # On travis this warning is an error to prevent merging incomplete commits into master
+ # On CI this warning is an error to prevent merging incomplete commits into master
sys.exit(1)
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index fbcf21e729..355cd7af75 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -1,18 +1,25 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 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 bitcoin-wallet."""
+
+import hashlib
+import os
+import stat
import subprocess
import textwrap
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+BUFFER_SIZE = 16 * 1024
+
class ToolWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -32,23 +39,54 @@ class ToolWalletTest(BitcoinTestFramework):
def assert_tool_output(self, output, *args):
p = self.bitcoin_wallet_process(*args)
stdout, stderr = p.communicate()
- assert_equal(p.poll(), 0)
assert_equal(stderr, '')
assert_equal(stdout, output)
+ assert_equal(p.poll(), 0)
- def run_test(self):
+ def wallet_shasum(self):
+ h = hashlib.sha1()
+ mv = memoryview(bytearray(BUFFER_SIZE))
+ with open(self.wallet_path, 'rb', buffering=0) as f:
+ for n in iter(lambda : f.readinto(mv), 0):
+ h.update(mv[:n])
+ return h.hexdigest()
+ def wallet_timestamp(self):
+ return os.path.getmtime(self.wallet_path)
+
+ def wallet_permissions(self):
+ return oct(os.lstat(self.wallet_path).st_mode)[-3:]
+
+ def log_wallet_timestamp_comparison(self, old, new):
+ result = 'unchanged' if new == old else 'increased!'
+ self.log.debug('Wallet file timestamp {}'.format(result))
+
+ def test_invalid_tool_commands_and_args(self):
+ self.log.info('Testing that various invalid commands raise with specific error messages')
self.assert_raises_tool_error('Invalid command: foo', 'foo')
- # `bitcoin-wallet help` is an error. Use `bitcoin-wallet -help`
+ # `bitcoin-wallet help` raises an error. Use `bitcoin-wallet -help`.
self.assert_raises_tool_error('Invalid command: help', 'help')
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
self.assert_raises_tool_error('Error loading wallet.dat. Is wallet being used by other process?', '-wallet=wallet.dat', 'info')
self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
- # stop the node to close the wallet to call info command
+ def test_tool_wallet_info(self):
+ # Stop the node to close the wallet to call the info command.
self.stop_node(0)
-
+ self.log.info('Calling wallet tool info, testing output')
+ #
+ # TODO: Wallet tool info should work with wallet file permissions set to
+ # read-only without raising:
+ # "Error loading wallet.dat. Is wallet being used by another process?"
+ # The following lines should be uncommented and the tests still succeed:
+ #
+ # self.log.debug('Setting wallet file permissions to 400 (read-only)')
+ # os.chmod(self.wallet_path, stat.S_IRUSR)
+ # assert(self.wallet_permissions() in ['400', '666']) # Sanity check. 666 because Appveyor.
+ # shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
out = textwrap.dedent('''\
Wallet info
===========
@@ -59,12 +97,35 @@ class ToolWalletTest(BitcoinTestFramework):
Address Book: 3
''')
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
-
- # mutate the wallet to check the info command output changes accordingly
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ self.log.debug('Setting wallet file permissions back to 600 (read/write)')
+ os.chmod(self.wallet_path, stat.S_IRUSR | stat.S_IWUSR)
+ assert(self.wallet_permissions() in ['600', '666']) # Sanity check. 666 because Appveyor.
+ #
+ # TODO: Wallet tool info should not write to the wallet file.
+ # The following lines should be uncommented and the tests still succeed:
+ #
+ # assert_equal(timestamp_before, timestamp_after)
+ # shasum_after = self.wallet_shasum()
+ # assert_equal(shasum_before, shasum_after)
+ # self.log.debug('Wallet file shasum unchanged\n')
+
+ def test_tool_wallet_info_after_transaction(self):
+ """
+ Mutate the wallet with a transaction to verify that the info command
+ output changes accordingly.
+ """
self.start_node(0)
+ self.log.info('Generating transaction to mutate wallet')
self.nodes[0].generate(1)
self.stop_node(0)
+ self.log.info('Calling wallet tool info after generating a transaction, testing output')
+ shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
out = textwrap.dedent('''\
Wallet info
===========
@@ -75,7 +136,22 @@ class ToolWalletTest(BitcoinTestFramework):
Address Book: 3
''')
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
-
+ shasum_after = self.wallet_shasum()
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ #
+ # TODO: Wallet tool info should not write to the wallet file.
+ # This assertion should be uncommented and succeed:
+ # assert_equal(timestamp_before, timestamp_after)
+ assert_equal(shasum_before, shasum_after)
+ self.log.debug('Wallet file shasum unchanged\n')
+
+ def test_tool_wallet_create_on_existing_wallet(self):
+ self.log.info('Calling wallet tool create on an existing wallet, testing output')
+ shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling create: {}'.format(timestamp_before))
out = textwrap.dedent('''\
Topping up keypool...
Wallet info
@@ -87,15 +163,48 @@ class ToolWalletTest(BitcoinTestFramework):
Address Book: 0
''')
self.assert_tool_output(out, '-wallet=foo', 'create')
-
+ shasum_after = self.wallet_shasum()
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling create: {}'.format(timestamp_after))
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ assert_equal(timestamp_before, timestamp_after)
+ assert_equal(shasum_before, shasum_after)
+ self.log.debug('Wallet file shasum unchanged\n')
+
+ def test_getwalletinfo_on_different_wallet(self):
+ self.log.info('Starting node with arg -wallet=foo')
self.start_node(0, ['-wallet=foo'])
+
+ self.log.info('Calling getwalletinfo on a different wallet ("foo"), testing output')
+ shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling getwalletinfo: {}'.format(timestamp_before))
out = self.nodes[0].getwalletinfo()
self.stop_node(0)
+ shasum_after = self.wallet_shasum()
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling getwalletinfo: {}'.format(timestamp_after))
+
assert_equal(0, out['txcount'])
assert_equal(1000, out['keypoolsize'])
assert_equal(1000, out['keypoolsize_hd_internal'])
assert_equal(True, 'hdseedid' in out)
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ assert_equal(timestamp_before, timestamp_after)
+ assert_equal(shasum_after, shasum_before)
+ self.log.debug('Wallet file shasum unchanged\n')
+
+ def run_test(self):
+ self.wallet_path = os.path.join(self.nodes[0].datadir, 'regtest', 'wallets', 'wallet.dat')
+ self.test_invalid_tool_commands_and_args()
+ # Warning: The following tests are order-dependent.
+ self.test_tool_wallet_info()
+ self.test_tool_wallet_info_after_transaction()
+ self.test_tool_wallet_create_on_existing_wallet()
+ self.test_getwalletinfo_on_different_wallet()
+
+
if __name__ == '__main__':
ToolWalletTest().main()
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index a40613dfc7..f8318555b1 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -64,7 +64,10 @@ from test_framework.util import (
assert_raises_rpc_error,
connect_nodes_bi,
)
-
+from test_framework.segwit_addr import (
+ encode,
+ decode,
+)
class AddressTypeTest(BitcoinTestFramework):
def set_test_params(self):
@@ -97,6 +100,13 @@ class AddressTypeTest(BitcoinTestFramework):
else:
return [self.nodes[i].getunconfirmedbalance() for i in range(4)]
+ # Quick test of python bech32 implementation
+ def test_python_bech32(self, addr):
+ hrp = addr[:4]
+ assert_equal(hrp, "bcrt")
+ (witver, witprog) = decode(hrp, addr)
+ assert_equal(encode(hrp, witver, witprog), addr)
+
def test_address(self, node, address, multisig, typ):
"""Run sanity checks on an address."""
info = self.nodes[node].getaddressinfo(address)
@@ -121,6 +131,7 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 40)
assert 'pubkey' in info
+ self.test_python_bech32(info["address"])
elif typ == 'legacy':
# P2SH-multisig
assert info['isscript']
@@ -146,6 +157,7 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 64)
assert 'pubkeys' in info
+ self.test_python_bech32(info["address"])
else:
# Unknown type
assert False
@@ -175,6 +187,10 @@ class AddressTypeTest(BitcoinTestFramework):
assert info['desc'] == descsum_create(info['desc'][:-9])
# Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips
assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor']
+ assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['checksum'])
+ # Verify that keeping the checksum and feeding it to getdescriptorinfo roundtrips
+ assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'])['descriptor']
+ assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'])['checksum'])
if not multisig and typ == 'legacy':
# P2PKH
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
new file mode 100755
index 0000000000..58ad835d39
--- /dev/null
+++ b/test/functional/wallet_avoidreuse.py
@@ -0,0 +1,241 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 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 the avoid_reuse and setwalletflag features."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+)
+
+# TODO: Copied from wallet_groups.py -- should perhaps move into util.py
+def assert_approx(v, vexp, vspan=0.00001):
+ if v < vexp - vspan:
+ raise AssertionError("%s < [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+ if v > vexp + vspan:
+ raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+
+def reset_balance(node, discardaddr):
+ '''Throw away all owned coins by the node so it gets a balance of 0.'''
+ balance = node.getbalance(avoid_reuse=False)
+ if balance > 0.5:
+ node.sendtoaddress(address=discardaddr, amount=balance, subtractfeefromamount=True, avoid_reuse=False)
+
+def count_unspent(node):
+ '''Count the unspent outputs for the given node and return various statistics'''
+ r = {
+ "total": {
+ "count": 0,
+ "sum": 0,
+ },
+ "reused": {
+ "count": 0,
+ "sum": 0,
+ },
+ }
+ supports_reused = True
+ for utxo in node.listunspent(minconf=0):
+ r["total"]["count"] += 1
+ r["total"]["sum"] += utxo["amount"]
+ if supports_reused and "reused" in utxo:
+ if utxo["reused"]:
+ r["reused"]["count"] += 1
+ r["reused"]["sum"] += utxo["amount"]
+ else:
+ supports_reused = False
+ r["reused"]["supported"] = supports_reused
+ return r
+
+def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None):
+ '''Make assertions about a node's unspent output statistics'''
+ stats = count_unspent(node)
+ if total_count is not None:
+ assert_equal(stats["total"]["count"], total_count)
+ if total_sum is not None:
+ assert_approx(stats["total"]["sum"], total_sum, 0.001)
+ if reused_supported is not None:
+ assert_equal(stats["reused"]["supported"], reused_supported)
+ if reused_count is not None:
+ assert_equal(stats["reused"]["count"], reused_count)
+ if reused_sum is not None:
+ assert_approx(stats["reused"]["sum"], reused_sum, 0.001)
+
+def assert_balances(node, mine):
+ '''Make assertions about a node's getbalances output'''
+ got = node.getbalances()["mine"]
+ for k,v in mine.items():
+ assert_approx(got[k], v, 0.001)
+
+class AvoidReuseTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ '''Set up initial chain and run tests defined below'''
+
+ self.test_persistence()
+ self.test_immutable()
+
+ self.nodes[0].generate(110)
+ self.sync_all()
+ reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
+ self.test_fund_send_fund_senddirty()
+ reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
+ self.test_fund_send_fund_send()
+
+ def test_persistence(self):
+ '''Test that wallet files persist the avoid_reuse flag.'''
+ # Configure node 1 to use avoid_reuse
+ self.nodes[1].setwalletflag('avoid_reuse')
+
+ # Flags should be node1.avoid_reuse=false, node2.avoid_reuse=true
+ assert_equal(self.nodes[0].getwalletinfo()["avoid_reuse"], False)
+ assert_equal(self.nodes[1].getwalletinfo()["avoid_reuse"], True)
+
+ # Stop and restart node 1
+ self.stop_node(1)
+ self.start_node(1)
+ connect_nodes_bi(self.nodes, 0, 1)
+
+ # Flags should still be node1.avoid_reuse=false, node2.avoid_reuse=true
+ assert_equal(self.nodes[0].getwalletinfo()["avoid_reuse"], False)
+ assert_equal(self.nodes[1].getwalletinfo()["avoid_reuse"], True)
+
+ # Attempting to set flag to its current state should throw
+ assert_raises_rpc_error(-8, "Wallet flag is already set to false", self.nodes[0].setwalletflag, 'avoid_reuse', False)
+ assert_raises_rpc_error(-8, "Wallet flag is already set to true", self.nodes[1].setwalletflag, 'avoid_reuse', True)
+
+ def test_immutable(self):
+ '''Test immutable wallet flags'''
+ # Attempt to set the disable_private_keys flag; this should not work
+ assert_raises_rpc_error(-8, "Wallet flag is immutable", self.nodes[1].setwalletflag, 'disable_private_keys')
+
+ tempwallet = ".wallet_avoidreuse.py_test_immutable_wallet.dat"
+
+ # Create a wallet with disable_private_keys set; this should work
+ self.nodes[1].createwallet(tempwallet, True)
+ w = self.nodes[1].get_wallet_rpc(tempwallet)
+
+ # Attempt to unset the disable_private_keys flag; this should not work
+ assert_raises_rpc_error(-8, "Wallet flag is immutable", w.setwalletflag, 'disable_private_keys', False)
+
+ # Unload temp wallet
+ self.nodes[1].unloadwallet(tempwallet)
+
+ def test_fund_send_fund_senddirty(self):
+ '''
+ Test the same as test_fund_send_fund_send, except send the 10 BTC with
+ the avoid_reuse flag set to false. This means the 10 BTC send should succeed,
+ where it fails in test_fund_send_fund_send.
+ '''
+
+ fundaddr = self.nodes[1].getnewaddress()
+ retaddr = self.nodes[0].getnewaddress()
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 10 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0)
+ # getbalances should show no used, 10 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10})
+ # node 0 should not show a used entry, as it does not enable avoid_reuse
+ assert("used" not in self.nodes[0].getbalances()["mine"])
+
+ self.nodes[1].sendtoaddress(retaddr, 5)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 5 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0)
+ # getbalances should show no used, 5 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5})
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10)
+ assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10)
+ # getbalances should show 10 used, 5 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5})
+
+ self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False)
+
+ # listunspent should show 1 total outputs (5 btc), unused
+ assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0)
+ # getbalances should show no used, 5 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5})
+
+ # node 1 should now have about 5 btc left (for both cases)
+ assert_approx(self.nodes[1].getbalance(), 5, 0.001)
+ assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 5, 0.001)
+
+ def test_fund_send_fund_send(self):
+ '''
+ Test the simple case where [1] generates a new address A, then
+ [0] sends 10 BTC to A.
+ [1] spends 5 BTC from A. (leaving roughly 5 BTC useable)
+ [0] sends 10 BTC to A again.
+ [1] tries to spend 10 BTC (fails; dirty).
+ [1] tries to spend 4 BTC (succeeds; change address sufficient)
+ '''
+
+ fundaddr = self.nodes[1].getnewaddress()
+ retaddr = self.nodes[0].getnewaddress()
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 10 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0)
+ # getbalances should show no used, 10 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10})
+
+ self.nodes[1].sendtoaddress(retaddr, 5)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 5 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0)
+ # getbalances should show no used, 5 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5})
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10)
+ assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10)
+ # getbalances should show 10 used, 5 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5})
+
+ # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty)
+ assert_approx(self.nodes[1].getbalance(), 5, 0.001)
+ assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001)
+
+ assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10)
+
+ self.nodes[1].sendtoaddress(retaddr, 4)
+
+ # listunspent should show 2 total outputs (1, 10 btc), one unused (1), one reused (10)
+ assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10)
+ # getbalances should show 10 used, 1 btc trusted
+ assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1})
+
+ # node 1 should now have about 1 btc left (no dirty) and 11 (including dirty)
+ assert_approx(self.nodes[1].getbalance(), 1, 0.001)
+ assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001)
+
+if __name__ == '__main__':
+ AvoidReuseTest().main()
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index 55c517e92f..93178c5ab2 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -49,6 +49,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.setup_clean_chain = True
# nodes 1, 2,3 are spenders, let's give them a keypool=100
self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
+ self.rpc_timeout = 120
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 15f2195e21..137c77a51e 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -203,6 +203,8 @@ class WalletTest(BitcoinTestFramework):
self.log.info('Put txs back into mempool of node 1 (not node 0)')
self.nodes[0].invalidateblock(block_reorg)
self.nodes[1].invalidateblock(block_reorg)
+ self.sync_blocks()
+ self.nodes[0].syncwithvalidationinterfacequeue()
assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
self.nodes[0].generatetoaddress(1, ADDRESS_WATCHONLY)
assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index daa834b5b8..ce04110240 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -20,6 +20,9 @@ from test_framework.util import (
class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ ]] * self.num_nodes
self.setup_clean_chain = True
def skip_test_if_missing_module(self):
@@ -496,6 +499,36 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].setlabel(change, 'foobar')
assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)
+ # Test gettransaction response with different arguments.
+ self.log.info("Testing gettransaction response with different arguments...")
+ self.nodes[0].setlabel(change, 'baz')
+ baz = self.nodes[0].listtransactions(label="baz", count=1)[0]
+ expected_receive_vout = {"label": "baz",
+ "address": baz["address"],
+ "amount": baz["amount"],
+ "category": baz["category"],
+ "vout": baz["vout"]}
+ expected_fields = frozenset({'amount', 'bip125-replaceable', 'confirmations', 'details', 'fee',
+ 'hex', 'time', 'timereceived', 'trusted', 'txid', 'walletconflicts'})
+ verbose_field = "decoded"
+ expected_verbose_fields = expected_fields | {verbose_field}
+
+ self.log.debug("Testing gettransaction response without verbose")
+ tx = self.nodes[0].gettransaction(txid=txid)
+ assert_equal(set([*tx]), expected_fields)
+ assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
+
+ self.log.debug("Testing gettransaction response with verbose set to False")
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=False)
+ assert_equal(set([*tx]), expected_fields)
+ assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
+
+ self.log.debug("Testing gettransaction response with verbose set to True")
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=True)
+ assert_equal(set([*tx]), expected_verbose_fields)
+ assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
+ assert_equal(tx[verbose_field], self.nodes[0].decoderawtransaction(tx["hex"]))
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 568b1f28d8..bfc01e3f5e 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -37,6 +37,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.extra_args = [[
"-walletrbf={}".format(i),
"-mintxfee=0.00002",
+ "-deprecatedrpc=totalFee",
] for i in range(self.num_nodes)]
def skip_test_if_missing_module(self):
@@ -80,6 +81,7 @@ class BumpFeeTest(BitcoinTestFramework):
test_bumpfee_metadata(rbf_node, dest_address)
test_locked_wallet_fails(rbf_node, dest_address)
test_change_script_match(rbf_node, dest_address)
+ test_maxtxfee_fails(self, rbf_node, dest_address)
# These tests wipe out a number of utxos that are expected in other tests
test_small_output_with_feerate_succeeds(rbf_node, dest_address)
test_no_more_inputs_fails(rbf_node, dest_address)
@@ -248,6 +250,15 @@ def test_settxfee(rbf_node, dest_address):
rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
+def test_maxtxfee_fails(test, rbf_node, dest_address):
+ test.restart_node(1, ['-maxtxfee=0.00003'] + test.extra_args[1])
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+ rbfid = spend_one_input(rbf_node, dest_address)
+ assert_raises_rpc_error(-4, "Unable to create transaction: Fee exceeds maximum configured by -maxtxfee", rbf_node.bumpfee, rbfid)
+ test.restart_node(1, test.extra_args[1])
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+
+
def test_rebumping(rbf_node, dest_address):
# check that re-bumping the original tx fails, but bumping the bumper succeeds
rbfid = spend_one_input(rbf_node, dest_address)
@@ -304,7 +315,9 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
def test_bumpfee_metadata(rbf_node, dest_address):
- rbfid = rbf_node.sendtoaddress(dest_address, Decimal("0.00100000"), "comment value", "to value")
+ assert(rbf_node.getbalance() < 49)
+ rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
+ rbfid = rbf_node.sendtoaddress(dest_address, 49, "comment value", "to value")
bumped_tx = rbf_node.bumpfee(rbfid)
bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
assert_equal(bumped_wtx["comment"], "comment value")
@@ -360,7 +373,7 @@ def submit_block_with_tx(node, tx):
block.hashMerkleRoot = block.calc_merkle_root()
add_witness_commitment(block)
block.solve()
- node.submitblock(block.serialize(True).hex())
+ node.submitblock(block.serialize().hex())
return block
def test_no_more_inputs_fails(rbf_node, dest_address):
diff --git a/test/functional/wallet_bumpfee_totalfee_deprecation.py b/test/functional/wallet_bumpfee_totalfee_deprecation.py
new file mode 100755
index 0000000000..b8e097c32e
--- /dev/null
+++ b/test/functional/wallet_bumpfee_totalfee_deprecation.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 deprecation of passing `totalFee` to the bumpfee RPC."""
+from decimal import Decimal
+
+from test_framework.messages import BIP125_SEQUENCE_NUMBER
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
+
+class BumpFeeWithTotalFeeArgumentDeprecationTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.extra_args = [[
+ "-walletrbf={}".format(i),
+ "-mintxfee=0.00002",
+ ] for i in range(self.num_nodes)]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ peer_node, rbf_node = self.nodes
+ peer_node.generate(110)
+ self.sync_all()
+ peer_node.sendtoaddress(rbf_node.getnewaddress(), 0.001)
+ self.sync_all()
+ peer_node.generate(1)
+ self.sync_all()
+ rbfid = spend_one_input(rbf_node, peer_node.getnewaddress())
+
+ self.log.info("Testing bumpfee with totalFee argument raises RPC error with deprecation message")
+ assert_raises_rpc_error(
+ -8,
+ "totalFee argument has been deprecated and will be removed in 0.20. " +
+ "Please use -deprecatedrpc=totalFee to continue using this argument until removal.",
+ rbf_node.bumpfee, rbfid, {"totalFee": 2000})
+
+ self.log.info("Testing bumpfee without totalFee argument does not raise")
+ rbf_node.bumpfee(rbfid)
+
+def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
+ tx_input = dict(sequence=BIP125_SEQUENCE_NUMBER,
+ **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
+ destinations = {dest_address: Decimal("0.00050000")}
+ destinations[node.getrawchangeaddress()] = change_size
+ rawtx = node.createrawtransaction([tx_input], destinations)
+ signedtx = node.signrawtransactionwithwallet(rawtx)
+ txid = node.sendrawtransaction(signedtx["hex"])
+ return txid
+
+if __name__ == "__main__":
+ BumpFeeWithTotalFeeArgumentDeprecationTest().main()
diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py
index 0b584a0bb2..330de8b0fc 100755
--- a/test/functional/wallet_create_tx.py
+++ b/test/functional/wallet_create_tx.py
@@ -6,6 +6,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_raises_rpc_error,
)
from test_framework.blocktools import (
TIME_GENESIS_BLOCK,
@@ -26,6 +27,10 @@ class CreateTxWalletTest(BitcoinTestFramework):
self.nodes[0].generate(200)
self.nodes[0].setmocktime(0)
+ self.test_anti_fee_sniping()
+ self.test_tx_size_too_large()
+
+ def test_anti_fee_sniping(self):
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
@@ -38,6 +43,40 @@ class CreateTxWalletTest(BitcoinTestFramework):
tx = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(txid)['hex'])
assert 0 < tx['locktime'] <= 201
+ def test_tx_size_too_large(self):
+ # More than 10kB of outputs, so that we hit -maxtxfee with a high feerate
+ outputs = {self.nodes[0].getnewaddress(address_type='bech32'): 0.000025 for i in range(400)}
+ raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)
+
+ for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
+ self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
+ self.restart_node(0, extra_args=[fee_setting])
+ assert_raises_rpc_error(
+ -6,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
+ )
+ assert_raises_rpc_error(
+ -4,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
+ )
+
+ self.log.info('Check maxtxfee in combination with settxfee')
+ self.restart_node(0)
+ self.nodes[0].settxfee(0.01)
+ assert_raises_rpc_error(
+ -6,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
+ )
+ assert_raises_rpc_error(
+ -4,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
+ )
+ self.nodes[0].settxfee(0)
+
if __name__ == '__main__':
CreateTxWalletTest().main()
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index fc7969ad19..e302e499f4 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -92,7 +92,7 @@ class CreateWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getnewaddress)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getrawchangeaddress)
# Encrypt the wallet
- w5.encryptwallet('pass')
+ assert_raises_rpc_error(-16, "Error: wallet does not contain private keys, nothing to encrypt.", w5.encryptwallet, 'pass')
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getnewaddress)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getrawchangeaddress)
@@ -116,8 +116,20 @@ class CreateWalletTest(BitcoinTestFramework):
walletinfo = w6.getwalletinfo()
assert_equal(walletinfo['keypoolsize'], 1)
assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
- # Empty passphrase, error
- assert_raises_rpc_error(-16, 'Cannot encrypt a wallet with a blank password', self.nodes[0].createwallet, 'w7', False, False, '')
+ # Allow empty passphrase, but there should be a warning
+ resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='')
+ assert_equal(resp['warning'], 'Empty string given as passphrase, wallet will not be encrypted.')
+ w7 = node.get_wallet_rpc('w7')
+ assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10)
+
+ self.log.info('Test making a wallet with avoid reuse flag')
+ self.nodes[0].createwallet('w8', False, False, '', True) # Use positional arguments to check for bug where avoid_reuse could not be set for wallets without needing them to be encrypted
+ w8 = node.get_wallet_rpc('w8')
+ assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10)
+ assert_equal(w8.getwalletinfo()["avoid_reuse"], True)
+
+ self.log.info('Using a passphrase with private keys disabled returns error')
+ assert_raises_rpc_error(-4, 'Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.', self.nodes[0].createwallet, wallet_name='w9', disable_private_keys=True, passphrase='thisisapassphrase')
if __name__ == '__main__':
CreateWalletTest().main()
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index c514b7e0b4..fbcb4e75ba 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -49,7 +49,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_equal(privkey, self.nodes[0].dumpprivkey(address))
# Check that the timeout is right
- time.sleep(2)
+ time.sleep(3)
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
# Test wrong passphrase
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index 47c97f62bf..4e20892596 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -20,6 +20,7 @@ happened previously.
"""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.address import AddressType
from test_framework.util import (
connect_nodes,
assert_equal,
@@ -27,21 +28,30 @@ from test_framework.util import (
)
import collections
+from decimal import Decimal
import enum
import itertools
+import random
Call = enum.Enum("Call", "single multiaddress multiscript")
Data = enum.Enum("Data", "address pub priv")
Rescan = enum.Enum("Rescan", "no yes late_timestamp")
-class Variant(collections.namedtuple("Variant", "call data rescan prune")):
+class Variant(collections.namedtuple("Variant", "call data address_type rescan prune")):
"""Helper for importing one key and verifying scanned transactions."""
def do_import(self, timestamp):
"""Call one key import RPC."""
rescan = self.rescan == Rescan.yes
+ assert_equal(self.address["solvable"], True)
+ assert_equal(self.address["isscript"], self.address_type == AddressType.p2sh_segwit)
+ assert_equal(self.address["iswitness"], self.address_type == AddressType.bech32)
+ if self.address["isscript"]:
+ assert_equal(self.address["embedded"]["isscript"], False)
+ assert_equal(self.address["embedded"]["iswitness"], True)
+
if self.call == Call.single:
if self.data == Data.address:
response = self.node.importaddress(address=self.address["address"], label=self.label, rescan=rescan)
@@ -52,7 +62,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
assert_equal(response, None)
elif self.call in (Call.multiaddress, Call.multiscript):
- response = self.node.importmulti([{
+ request = {
"scriptPubKey": {
"address": self.address["address"]
} if self.call == Call.multiaddress else self.address["scriptPubKey"],
@@ -61,13 +71,21 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
"keys": [self.key] if self.data == Data.priv else [],
"label": self.label,
"watchonly": self.data != Data.priv
- }], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)})
+ }
+ if self.address_type == AddressType.p2sh_segwit and self.data != Data.address:
+ # We need solving data when providing a pubkey or privkey as data
+ request.update({"redeemscript": self.address['embedded']['scriptPubKey']})
+ response = self.node.importmulti(
+ requests=[request],
+ options={"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)},
+ )
assert_equal(response, [{"success": True}])
- def check(self, txid=None, amount=None, confirmations=None):
+ def check(self, txid=None, amount=None, confirmation_height=None):
"""Verify that listtransactions/listreceivedbyaddress return expected values."""
txs = self.node.listtransactions(label=self.label, count=10000, include_watchonly=True)
+ current_height = self.node.getblockcount()
assert_equal(len(txs), self.expected_txs)
addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address'])
@@ -82,13 +100,13 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
assert_equal(tx["category"], "receive")
assert_equal(tx["label"], self.label)
assert_equal(tx["txid"], txid)
- assert_equal(tx["confirmations"], confirmations)
+ assert_equal(tx["confirmations"], 1 + current_height - confirmation_height)
assert_equal("trusted" not in tx, True)
address, = [ad for ad in addresses if txid in ad["txids"]]
assert_equal(address["address"], self.address["address"])
assert_equal(address["amount"], self.expected_balance)
- assert_equal(address["confirmations"], confirmations)
+ assert_equal(address["confirmations"], 1 + current_height - confirmation_height)
# Verify the transaction is correctly marked watchonly depending on
# whether the transaction pays to an imported public key or
# imported private key. The test setup ensures that transaction
@@ -102,7 +120,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
# List of Variants for each way a key or address could be imported.
-IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan, (False, True))]
+IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, AddressType, Rescan, (False, True))]
# List of nodes to import keys to. Half the nodes will have pruning disabled,
# half will have it enabled. Different nodes will be used for imports that are
@@ -116,6 +134,13 @@ IMPORT_NODES = [ImportNode(*fields) for fields in itertools.product((False, True
# Rescans start at the earliest block up to 2 hours before the key timestamp.
TIMESTAMP_WINDOW = 2 * 60 * 60
+AMOUNT_DUST = 0.00000546
+
+
+def get_rand_amount():
+ r = random.uniform(AMOUNT_DUST, 1)
+ return Decimal(str(round(r, 8)))
+
class ImportRescanTest(BitcoinTestFramework):
def set_test_params(self):
@@ -125,12 +150,12 @@ class ImportRescanTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def setup_network(self):
- extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)]
+ self.extra_args = [[]] * self.num_nodes
for i, import_node in enumerate(IMPORT_NODES, 2):
if import_node.prune:
- extra_args[i] += ["-prune=1"]
+ self.extra_args[i] += ["-prune=1"]
- self.add_nodes(self.num_nodes, extra_args=extra_args)
+ self.add_nodes(self.num_nodes, extra_args=self.extra_args)
# Import keys with pruning disabled
self.start_nodes(extra_args=[[]] * self.num_nodes)
@@ -147,17 +172,23 @@ class ImportRescanTest(BitcoinTestFramework):
# each possible type of wallet import RPC.
for i, variant in enumerate(IMPORT_VARIANTS):
variant.label = "label {} {}".format(i, variant)
- variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label))
+ variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(
+ label=variant.label,
+ address_type=variant.address_type.value,
+ ))
variant.key = self.nodes[1].dumpprivkey(variant.address["address"])
- variant.initial_amount = 1 - (i + 1) / 64
+ variant.initial_amount = get_rand_amount()
variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount)
+ self.nodes[0].generate(1) # Generate one block for each send
+ variant.confirmation_height = self.nodes[0].getblockcount()
+ variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
- # Generate a block containing the initial transactions, then another
- # block further in the future (past the rescan window).
- self.nodes[0].generate(1)
+ # Generate a block further in the future (past the rescan window).
assert_equal(self.nodes[0].getrawmempool(), [])
- timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
- set_node_times(self.nodes, timestamp + TIMESTAMP_WINDOW + 1)
+ set_node_times(
+ self.nodes,
+ self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] + TIMESTAMP_WINDOW + 1,
+ )
self.nodes[0].generate(1)
self.sync_all()
@@ -167,11 +198,11 @@ class ImportRescanTest(BitcoinTestFramework):
self.log.info('Run import for variant {}'.format(variant))
expect_rescan = variant.rescan == Rescan.yes
variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))]
- variant.do_import(timestamp)
+ variant.do_import(variant.timestamp)
if expect_rescan:
variant.expected_balance = variant.initial_amount
variant.expected_txs = 1
- variant.check(variant.initial_txid, variant.initial_amount, 2)
+ variant.check(variant.initial_txid, variant.initial_amount, variant.confirmation_height)
else:
variant.expected_balance = 0
variant.expected_txs = 0
@@ -179,11 +210,11 @@ class ImportRescanTest(BitcoinTestFramework):
# Create new transactions sending to each address.
for i, variant in enumerate(IMPORT_VARIANTS):
- variant.sent_amount = 1 - (2 * i + 1) / 128
+ variant.sent_amount = get_rand_amount()
variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount)
+ self.nodes[0].generate(1) # Generate one block for each send
+ variant.confirmation_height = self.nodes[0].getblockcount()
- # Generate a block containing the new transactions.
- self.nodes[0].generate(1)
assert_equal(self.nodes[0].getrawmempool(), [])
self.sync_all()
@@ -192,7 +223,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.log.info('Run check for variant {}'.format(variant))
variant.expected_balance += variant.sent_amount
variant.expected_txs += 1
- variant.check(variant.sent_txid, variant.sent_amount, 1)
+ variant.check(variant.sent_txid, variant.sent_amount, variant.confirmation_height)
if __name__ == "__main__":
ImportRescanTest().main()
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index 7d652a7825..23748e5dd7 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -44,8 +44,10 @@ class ImportMultiTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]):
+ def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=None):
"""Run importmulti and assert success"""
+ if warnings is None:
+ warnings = []
result = self.nodes[1].importmulti([req])
observed_warnings = []
if 'warnings' in result[0]:
@@ -552,7 +554,7 @@ class ImportMultiTest(BitcoinTestFramework):
"keys": [key.privkey]},
success=False,
error_code=-5,
- error_message="Descriptor is invalid")
+ error_message="Missing checksum")
# Test importing of a P2SH-P2WPKH address via descriptor + private key
key = get_key(self.nodes[0])
@@ -571,6 +573,7 @@ class ImportMultiTest(BitcoinTestFramework):
# Test ranged descriptor fails if range is not specified
xpriv = "tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg"
addresses = ["2N7yv4p8G8yEaPddJxY41kPihnWvs39qCMf", "2MsHxyb2JS3pAySeNUsJ7mNnurtpeenDzLA"] # hdkeypath=m/0'/0'/0' and 1'
+ addresses += ["bcrt1qrd3n235cj2czsfmsuvqqpr3lu6lg0ju7scl8gn", "bcrt1qfqeppuvj0ww98r6qghmdkj70tv8qpchehegrg8"] # wpkh subscripts corresponding to the above addresses
desc = "sh(wpkh(" + xpriv + "/0'/0'/*'" + "))"
self.log.info("Ranged descriptor import should fail without a specified range")
self.test_importmulti({"desc": descsum_create(desc),
@@ -579,17 +582,17 @@ class ImportMultiTest(BitcoinTestFramework):
error_code=-8,
error_message='Descriptor is ranged, please specify the range')
- # Test importing of a ranged descriptor without keys
+ # Test importing of a ranged descriptor with xpriv
self.log.info("Should import the ranged descriptor with specified range as solvable")
self.test_importmulti({"desc": descsum_create(desc),
"timestamp": "now",
"range": 1},
- success=True,
- warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ success=True)
for address in addresses:
test_address(self.nodes[1],
- key.p2sh_p2wpkh_addr,
- solvable=True)
+ address,
+ solvable=True,
+ ismine=True)
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
success=False, error_code=-8, error_message='End of range is too high')
@@ -606,6 +609,23 @@ class ImportMultiTest(BitcoinTestFramework):
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
success=False, error_code=-8, error_message='Range is too large')
+ # Test importing a descriptor containing a WIF private key
+ wif_priv = "cTe1f5rdT8A8DFgVWTjyPwACsDPJM9ff4QngFxUixCSvvbg1x6sh"
+ address = "2MuhcG52uHPknxDgmGPsV18jSHFBnnRgjPg"
+ desc = "sh(wpkh(" + wif_priv + "))"
+ self.log.info("Should import a descriptor with a WIF private key as spendable")
+ self.test_importmulti({"desc": descsum_create(desc),
+ "timestamp": "now"},
+ success=True)
+ test_address(self.nodes[1],
+ address,
+ solvable=True,
+ ismine=True)
+
+ # dump the private key to ensure it matches what was imported
+ privkey = self.nodes[1].dumpprivkey(address)
+ assert_equal(privkey, wif_priv)
+
# Test importing of a P2PKH address via descriptor
key = get_key(self.nodes[0])
self.log.info("Should import a p2pkh address from descriptor")
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 984ffab5a4..68bc45f986 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -17,6 +17,8 @@ from test_framework.util import (
assert_raises_rpc_error,
)
+FEATURE_LATEST = 169900
+
class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
@@ -27,6 +29,13 @@ class MultiWalletTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def add_options(self, parser):
+ parser.add_argument(
+ '--data_wallets_dir',
+ default=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/wallets/'),
+ help='Test data with wallet directories (default: %(default)s)',
+ )
+
def run_test(self):
node = self.nodes[0]
@@ -93,12 +102,12 @@ class MultiWalletTest(BitcoinTestFramework):
# should not initialize if one wallet is a copy of another
shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy'))
- exp_stderr = "BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
+ exp_stderr = r"BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
# should not initialize if wallet file is a symlink
os.symlink('w8', wallet_dir('w8_symlink'))
- self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], r'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
# should not initialize if the specified walletdir does not exist
self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
@@ -139,7 +148,7 @@ class MultiWalletTest(BitcoinTestFramework):
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
os.mkdir(competing_wallet_dir)
self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
- exp_stderr = "Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
+ exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.restart_node(0, extra_args)
@@ -323,6 +332,22 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].unloadwallet(wallet)
self.nodes[1].loadwallet(wallet)
+ # Fail to load if wallet is downgraded
+ shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
+ self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
+ assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
+ self.log.info("Fail -upgradewallet that results in downgrade")
+ assert_raises_rpc_error(
+ -4,
+ "Wallet loading failed.",
+ lambda: self.nodes[0].loadwallet(filename='high_minversion'),
+ )
+ self.stop_node(
+ i=0,
+ expected_stderr='Error: Error loading {}: Wallet requires newer version of Bitcoin Core'.format(
+ wallet_dir('high_minversion', 'wallet.dat')),
+ )
+
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py
new file mode 100755
index 0000000000..f48018e9fb
--- /dev/null
+++ b/test/functional/wallet_reorgsrestore.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 tx status in case of reorgs while wallet being shutdown.
+
+Wallet txn status rely on block connection/disconnection for its
+accuracy. In case of reorgs happening while wallet being shutdown
+block updates are not going to be received. At wallet loading, we
+check against chain if confirmed txn are still in chain and change
+their status if block in which they have been included has been
+disconnected.
+"""
+
+from decimal import Decimal
+import os
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ disconnect_nodes,
+)
+
+class ReorgsRestoreTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 3
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Send a tx from which to conflict outputs later
+ txid_conflict_from = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ # Disconnect node1 from others to reorg its chain later
+ disconnect_nodes(self.nodes[0], 1)
+ disconnect_nodes(self.nodes[1], 2)
+ connect_nodes(self.nodes[0], 2)
+
+ # Send a tx to be unconfirmed later
+ txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ tx = self.nodes[0].gettransaction(txid)
+ self.nodes[0].generate(4)
+ tx_before_reorg = self.nodes[0].gettransaction(txid)
+ assert_equal(tx_before_reorg["confirmations"], 4)
+
+ # Disconnect node0 from node2 to broadcast a conflict on their respective chains
+ disconnect_nodes(self.nodes[0], 2)
+ nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txid_conflict_from)["details"] if tx_out["amount"] == Decimal("10"))
+ inputs = []
+ inputs.append({"txid": txid_conflict_from, "vout": nA})
+ outputs_1 = {}
+ outputs_2 = {}
+
+ # Create a conflicted tx broadcast on node0 chain and conflicting tx broadcast on node1 chain. Both spend from txid_conflict_from
+ outputs_1[self.nodes[0].getnewaddress()] = Decimal("9.99998")
+ outputs_2[self.nodes[0].getnewaddress()] = Decimal("9.99998")
+ conflicted = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs_1))
+ conflicting = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs_2))
+
+ conflicted_txid = self.nodes[0].sendrawtransaction(conflicted["hex"])
+ self.nodes[0].generate(1)
+ conflicting_txid = self.nodes[2].sendrawtransaction(conflicting["hex"])
+ self.nodes[2].generate(9)
+
+ # Reconnect node0 and node2 and check that conflicted_txid is effectively conflicted
+ connect_nodes(self.nodes[0], 2)
+ self.sync_blocks([self.nodes[0], self.nodes[2]])
+ conflicted = self.nodes[0].gettransaction(conflicted_txid)
+ conflicting = self.nodes[0].gettransaction(conflicting_txid)
+ assert_equal(conflicted["confirmations"], -9)
+ assert_equal(conflicted["walletconflicts"][0], conflicting["txid"])
+
+ # Node0 wallet is shutdown
+ self.stop_node(0)
+ self.start_node(0)
+
+ # The block chain re-orgs and the tx is included in a different block
+ self.nodes[1].generate(9)
+ self.nodes[1].sendrawtransaction(tx["hex"])
+ self.nodes[1].generate(1)
+ self.nodes[1].sendrawtransaction(conflicted["hex"])
+ self.nodes[1].generate(1)
+
+ # Node0 wallet file is loaded on longest sync'ed node1
+ self.stop_node(1)
+ self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak'))
+ shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, 'regtest', 'wallet.dat'))
+ self.start_node(1)
+ tx_after_reorg = self.nodes[1].gettransaction(txid)
+ # Check that normal confirmed tx is confirmed again but with different blockhash
+ assert_equal(tx_after_reorg["confirmations"], 2)
+ assert(tx_before_reorg["blockhash"] != tx_after_reorg["blockhash"])
+ conflicted_after_reorg = self.nodes[1].gettransaction(conflicted_txid)
+ # Check that conflicted tx is confirmed again with blockhash different than previously conflicting tx
+ assert_equal(conflicted_after_reorg["confirmations"], 1)
+ assert(conflicting["blockhash"] != conflicted_after_reorg["blockhash"])
+
+if __name__ == '__main__':
+ ReorgsRestoreTest().main()
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 5810e94938..91d26e9cb3 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -57,8 +57,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
# after the last time we tried to broadcast. Use mocktime and give an extra minute to be sure.
block_time = int(time.time()) + 6 * 60
node.setmocktime(block_time)
- block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockchaininfo()['blocks']), block_time)
- block.nVersion = 3
+ block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockcount() + 1), block_time)
block.rehash()
block.solve()
node.submitblock(ToHex(block))
diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py
new file mode 100755
index 0000000000..be8d7714fb
--- /dev/null
+++ b/test/functional/wallet_watchonly.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018-2019 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 createwallet arguments.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error
+)
+
+
+class CreateWalletWatchonlyTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+ self.supports_cli = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ node = self.nodes[0]
+
+ self.nodes[0].createwallet(wallet_name='default')
+ def_wallet = node.get_wallet_rpc('default')
+
+ a1 = def_wallet.getnewaddress()
+ wo_change = def_wallet.getnewaddress()
+ wo_addr = def_wallet.getnewaddress()
+
+ self.nodes[0].createwallet(wallet_name='wo', disable_private_keys=True)
+ wo_wallet = node.get_wallet_rpc('wo')
+
+ wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_addr)['pubkey'])
+ wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_change)['pubkey'])
+
+ # generate some btc for testing
+ node.generatetoaddress(101, a1)
+
+ # send 1 btc to our watch-only address
+ txid = def_wallet.sendtoaddress(wo_addr, 1)
+ self.nodes[0].generate(1)
+
+ # getbalance
+ self.log.info('include_watchonly should default to true for watch-only wallets')
+ self.log.info('Testing getbalance watch-only defaults')
+ assert_equal(wo_wallet.getbalance(), 1)
+ assert_equal(len(wo_wallet.listtransactions()), 1)
+ assert_equal(wo_wallet.getbalance(include_watchonly=False), 0)
+
+ self.log.info('Testing listreceivedbyaddress watch-only defaults')
+ result = wo_wallet.listreceivedbyaddress()
+ assert_equal(len(result), 1)
+ assert_equal(result[0]["involvesWatchonly"], True)
+ result = wo_wallet.listreceivedbyaddress(include_watchonly=False)
+ assert_equal(len(result), 0)
+
+ self.log.info('Testing listreceivedbylabel watch-only defaults')
+ result = wo_wallet.listreceivedbylabel()
+ assert_equal(len(result), 1)
+ assert_equal(result[0]["involvesWatchonly"], True)
+ result = wo_wallet.listreceivedbylabel(include_watchonly=False)
+ assert_equal(len(result), 0)
+
+ self.log.info('Testing listtransactions watch-only defaults')
+ result = wo_wallet.listtransactions()
+ assert_equal(len(result), 1)
+ assert_equal(result[0]["involvesWatchonly"], True)
+ result = wo_wallet.listtransactions(include_watchonly=False)
+ assert_equal(len(result), 0)
+
+ self.log.info('Testing listsinceblock watch-only defaults')
+ result = wo_wallet.listsinceblock()
+ assert_equal(len(result["transactions"]), 1)
+ assert_equal(result["transactions"][0]["involvesWatchonly"], True)
+ result = wo_wallet.listsinceblock(include_watchonly=False)
+ assert_equal(len(result["transactions"]), 0)
+
+ self.log.info('Testing gettransaction watch-only defaults')
+ result = wo_wallet.gettransaction(txid)
+ assert_equal(result["details"][0]["involvesWatchonly"], True)
+ result = wo_wallet.gettransaction(txid=txid, include_watchonly=False)
+ assert_equal(len(result["details"]), 0)
+
+ self.log.info('Testing walletcreatefundedpsbt watch-only defaults')
+ inputs = []
+ outputs = [{a1: 0.5}]
+ options = {'changeAddress': wo_change}
+ no_wo_options = {'changeAddress': wo_change, 'includeWatching': False}
+
+ result = wo_wallet.walletcreatefundedpsbt(inputs=inputs, outputs=outputs, options=options)
+ assert_equal("psbt" in result, True)
+ assert_raises_rpc_error(-4, "Insufficient funds", wo_wallet.walletcreatefundedpsbt, inputs, outputs, 0, no_wo_options)
+
+ self.log.info('Testing fundrawtransaction watch-only defaults')
+ rawtx = wo_wallet.createrawtransaction(inputs=inputs, outputs=outputs)
+ result = wo_wallet.fundrawtransaction(hexstring=rawtx, options=options)
+ assert_equal("hex" in result, True)
+ assert_raises_rpc_error(-4, "Insufficient funds", wo_wallet.fundrawtransaction, rawtx, no_wo_options)
+
+
+
+if __name__ == '__main__':
+ CreateWalletWatchonlyTest().main()