aboutsummaryrefslogtreecommitdiff
path: root/test/functional/feature_taproot.py
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2020-10-01 23:24:00 -0700
committerPieter Wuille <pieter@wuille.net>2020-10-12 17:18:47 -0700
commit0e2a5e448f426219a6464b9aaadcc715534114e6 (patch)
treeb277fd73cf801b7066d1dee33b2b8858b98cb7b5 /test/functional/feature_taproot.py
parent4567ba034c5ae6e6cc161360f7425c9e844738f0 (diff)
downloadbitcoin-0e2a5e448f426219a6464b9aaadcc715534114e6.tar.xz
tests: dumping and minimizing of script assets data
This adds a --dumptests flag to the feature_taproot.py test, to dump all its generated test cases to files, in a format compatible with the script_assets_test unit test. A fuzzer for said format is added as well, whose primary purpose is coverage-based minimization of those dumps.
Diffstat (limited to 'test/functional/feature_taproot.py')
-rwxr-xr-xtest/functional/feature_taproot.py49
1 files changed, 48 insertions, 1 deletions
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 146193d018..7b534c1c2f 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -82,8 +82,11 @@ from test_framework.address import (
hash160,
sha256,
)
-from collections import namedtuple
+from collections import OrderedDict, namedtuple
from io import BytesIO
+import json
+import hashlib
+import os
import random
# === Framework for building spending transactions. ===
@@ -1142,10 +1145,52 @@ def spenders_taproot_inactive():
return spenders
+# Consensus validation flags to use in dumps for tests with "legacy/" or "inactive/" prefix.
+LEGACY_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY"
+# Consensus validation flags to use in dumps for all other tests.
+TAPROOT_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY,TAPROOT"
+
+def dump_json_test(tx, input_utxos, idx, success, failure):
+ spender = input_utxos[idx].spender
+ # Determine flags to dump
+ flags = LEGACY_FLAGS if spender.comment.startswith("legacy/") or spender.comment.startswith("inactive/") else TAPROOT_FLAGS
+
+ fields = [
+ ("tx", tx.serialize().hex()),
+ ("prevouts", [x.output.serialize().hex() for x in input_utxos]),
+ ("index", idx),
+ ("flags", flags),
+ ("comment", spender.comment)
+ ]
+
+ # The "final" field indicates that a spend should be always valid, even with more validation flags enabled
+ # than the listed ones. Use standardness as a proxy for this (which gives a conservative underestimate).
+ if spender.is_standard:
+ fields.append(("final", True))
+
+ def dump_witness(wit):
+ return OrderedDict([("scriptSig", wit[0].hex()), ("witness", [x.hex() for x in wit[1]])])
+ if success is not None:
+ fields.append(("success", dump_witness(success)))
+ if failure is not None:
+ fields.append(("failure", dump_witness(failure)))
+
+ # Write the dump to $TEST_DUMP_DIR/x/xyz... where x,y,z,... are the SHA1 sum of the dump (which makes the
+ # file naming scheme compatible with fuzzing infrastructure).
+ dump = json.dumps(OrderedDict(fields)) + ",\n"
+ sha1 = hashlib.sha1(dump.encode("utf-8")).hexdigest()
+ dirname = os.environ.get("TEST_DUMP_DIR", ".") + ("/%s" % sha1[0])
+ os.makedirs(dirname, exist_ok=True)
+ with open(dirname + ("/%s" % sha1), 'w', encoding="utf8") as f:
+ f.write(dump)
+
# Data type to keep track of UTXOs, where they were created, and how to spend them.
UTXOData = namedtuple('UTXOData', 'outpoint,output,spender')
class TaprootTest(BitcoinTestFramework):
+ def add_options(self, parser):
+ parser.add_argument("--dumptests", dest="dump_tests", default=False, action="store_true",
+ help="Dump generated test cases to directory set by TEST_DUMP_DIR environment variable")
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -1356,6 +1401,8 @@ class TaprootTest(BitcoinTestFramework):
if not input_utxos[i].spender.no_fail:
fail = fn(tx, i, [utxo.output for utxo in input_utxos], False)
input_data.append((fail, success))
+ if self.options.dump_tests:
+ dump_json_test(tx, input_utxos, i, success, fail)
# Sign each input incorrectly once on each complete signing pass, except the very last.
for fail_input in list(range(len(input_utxos))) + [None]: