52
52
OP_16 ,
53
53
OP_2DROP ,
54
54
OP_2DUP ,
55
+ OP_3DUP ,
55
56
OP_CHECKMULTISIG ,
56
57
OP_CHECKMULTISIGVERIFY ,
57
58
OP_CHECKSIG ,
58
59
OP_CHECKSIGADD ,
60
+ OP_CHECKSIGFROMSTACK ,
59
61
OP_CHECKSIGVERIFY ,
60
62
OP_CODESEPARATOR ,
61
63
OP_DROP ,
@@ -409,7 +411,7 @@ def default_scriptsig(ctx):
409
411
# The annex (only when mode=="taproot").
410
412
"annex" : None ,
411
413
# The codeseparator position (only when mode=="taproot").
412
- "codeseppos" : - 1 ,
414
+ "codeseppos" : 0xffffffff ,
413
415
# The redeemscript to add to the scriptSig (if P2SH; None implies not P2SH).
414
416
"script_p2sh" : None ,
415
417
# The script to add to the witness in (if P2WSH; None implies P2WPKH)
@@ -751,10 +753,12 @@ def spenders_taproot_active():
751
753
]
752
754
random .shuffle (scripts )
753
755
tap = taproot_construct (pubs [0 ], scripts )
754
- add_spender (spenders , "sighash/pk_codesep" , tap = tap , leaf = "pk_codesep" , key = secs [1 ], ** common , ** SINGLE_SIG , ** SIGHASH_BITFLIP , ** ERR_SIG_SCHNORR )
755
- add_spender (spenders , "sighash/codesep_pk" , tap = tap , leaf = "codesep_pk" , key = secs [1 ], codeseppos = 0 , ** common , ** SINGLE_SIG , ** SIGHASH_BITFLIP , ** ERR_SIG_SCHNORR )
756
- add_spender (spenders , "sighash/branched_codesep/left" , tap = tap , leaf = "branched_codesep" , key = secs [0 ], codeseppos = 3 , ** common , inputs = [getter ("sign" ), b'\x01 ' ], ** SIGHASH_BITFLIP , ** ERR_SIG_SCHNORR )
757
- add_spender (spenders , "sighash/branched_codesep/right" , tap = tap , leaf = "branched_codesep" , key = secs [1 ], codeseppos = 6 , ** common , inputs = [getter ("sign" ), b'' ], ** SIGHASH_BITFLIP , ** ERR_SIG_SCHNORR )
756
+ add_spender (spenders , "sighash/pk_codesep" , tap = tap , leaf = "pk_codesep" , key = secs [1 ], ** common , ** SINGLE_SIG , ** SIGHASH_BITFLIP , ** ERR_SCHNORR_SIG )
757
+ add_spender (spenders , "sighash/codesep_pk" , tap = tap , leaf = "codesep_pk" , key = secs [1 ], codeseppos = 0 , ** common , ** SINGLE_SIG , ** SIGHASH_BITFLIP , ** ERR_SCHNORR_SIG )
758
+ add_spender (spenders , "sighash/branched_codesep/left" , tap = tap , leaf = "branched_codesep" , key = secs [0 ], codeseppos = 3 , ** common , inputs = [getter ("sign" ), b'\x01 ' ], ** SIGHASH_BITFLIP , ** ERR_SCHNORR_SIG )
759
+ add_spender (spenders , "sighash/branched_codesep/right" , tap = tap , leaf = "branched_codesep" , key = secs [1 ], codeseppos = 6 , ** common , inputs = [getter ("sign" ), b'' ], ** SIGHASH_BITFLIP , ** ERR_SCHNORR_SIG )
760
+ add_spender (spenders , "sighash/codesep_pk_wrongpos1" , tap = tap , leaf = "codesep_pk" , key = secs [1 ], codeseppos = 0 , ** common , ** SINGLE_SIG , failure = {"codeseppos" : 1 }, ** ERR_SCHNORR_SIG )
761
+ add_spender (spenders , "sighash/codesep_pk_wrongpos2" , tap = tap , leaf = "codesep_pk" , key = secs [1 ], codeseppos = 0 , ** common , ** SINGLE_SIG , failure = {"codeseppos" : 0xfffffffe }, ** ERR_SCHNORR_SIG )
758
762
759
763
# Reusing the scripts above, test that various features affect the sighash.
760
764
add_spender (spenders , "sighash/annex" , tap = tap , leaf = "pk_codesep" , key = secs [1 ], hashtype = hashtype , standard = False , ** SINGLE_SIG , annex = bytes ([ANNEX_TAG ]), failure = {"sighash" : override (default_sighash , annex = None )}, ** ERR_SIG_SCHNORR )
@@ -1057,6 +1061,13 @@ def big_spend_inputs(ctx):
1057
1061
1058
1062
# == Test for sigops ratio limit ==
1059
1063
1064
+ # BIP348 CSFS signatures are embedded directly into the tapleaves vs the witness stack
1065
+ # since they do not introspect directly
1066
+ CSFS_MSG = b'\x00 \x00 '
1067
+ # Signature should pass even if random unknown key is used, just use real privkey
1068
+ # to pass in case it's the defined pubkey
1069
+ CSFS_SIG = sign_schnorr (secs [1 ], CSFS_MSG )
1070
+
1060
1071
# Given a number n, and a public key pk, functions that produce a (CScript, sigops). Each script takes as
1061
1072
# input a valid signature with the passed pk followed by a dummy push of bytes that are to be dropped, and
1062
1073
# will execute sigops signature checks.
@@ -1073,7 +1084,15 @@ def big_spend_inputs(ctx):
1073
1084
lambda n , pk : (CScript ([OP_DROP , OP_0 , pk , OP_CHECKSIG , OP_NOT , OP_VERIFY , pk ] + [OP_2DUP , OP_CHECKSIG , OP_VERIFY ] * n + [OP_CHECKSIG ]), n + 1 ),
1074
1085
# n OP_CHECKSIGADDs and 1 OP_CHECKSIG, but also an OP_CHECKSIGADD with an empty signature.
1075
1086
lambda n , pk : (CScript ([OP_DROP , OP_0 , OP_10 , pk , OP_CHECKSIGADD , OP_10 , OP_EQUALVERIFY , pk ] + [OP_2DUP , OP_16 , OP_SWAP , OP_CHECKSIGADD , b'\x11 ' , OP_EQUALVERIFY ] * n + [OP_CHECKSIG ]), n + 1 ),
1087
+ # n OP_CHECKSIGFROMSTACKs, dropping the signature given, and just validate against embedded sigs
1088
+ lambda n , pk : (CScript ([OP_2DROP , CSFS_SIG , CSFS_MSG , pk ] + [OP_3DUP , OP_CHECKSIGFROMSTACK , OP_DROP ] * n + [OP_2DROP ]), n ),
1089
+ # 1 CHECKSIGVERIFY followed by n OP_CHECKSIGFROMSTACKs, all signatures non-empty and validated
1090
+ lambda n , pk : (CScript ([OP_DROP , pk , OP_CHECKSIGVERIFY , CSFS_SIG , CSFS_MSG , pk ] + [OP_3DUP , OP_CHECKSIGFROMSTACK , OP_DROP ] * n + [OP_2DROP ]), n + 1 ),
1091
+ # 1 empty CHECKSIG followed by 1 empty OP_CHECKSIGFROMSTACKs, then finally n OP_CHECKSIGFROMSTACKs
1092
+ lambda n , pk : (CScript ([OP_2DROP , OP_0 , pk , OP_CHECKSIG , OP_DROP , OP_0 , CSFS_MSG , pk , OP_CHECKSIGFROMSTACK , OP_DROP , CSFS_SIG , CSFS_MSG , pk ] + [OP_3DUP , OP_CHECKSIGFROMSTACK , OP_DROP ] * n + [OP_2DROP ]), n ),
1093
+
1076
1094
]
1095
+
1077
1096
for annex in [None , bytes ([ANNEX_TAG ]) + random .randbytes (random .randrange (1000 ))]:
1078
1097
for hashtype in [SIGHASH_DEFAULT , SIGHASH_ALL ]:
1079
1098
for pubkey in [pubs [1 ], random .randbytes (random .choice ([x for x in range (2 , 81 ) if x != 32 ]))]:
@@ -1237,6 +1256,102 @@ def spenders_taproot_nonstandard():
1237
1256
1238
1257
return spenders
1239
1258
1259
+ def bip348_csfs_spenders ():
1260
+ secs = [generate_privkey () for _ in range (2 )]
1261
+ pubs = [compute_xonly_pubkey (sec )[0 ] for sec in secs ]
1262
+
1263
+ CSFS_MSG = random .randbytes (random .randrange (0 , 520 ))
1264
+
1265
+ # Grow, shrink the message being signed, and pick random bytes
1266
+ TRUNC_CSFS_MSG = CSFS_MSG [:] if len (CSFS_MSG ) > 0 else None
1267
+ if TRUNC_CSFS_MSG is not None :
1268
+ prune_index = random .randrange (len (TRUNC_CSFS_MSG ))
1269
+ TRUNC_CSFS_MSG = TRUNC_CSFS_MSG [:prune_index ] + TRUNC_CSFS_MSG [prune_index + 1 :]
1270
+ extendable_length = 520 - len (CSFS_MSG )
1271
+ EXTEND_CSFS_MSG = None
1272
+ if extendable_length > 0 :
1273
+ EXTEND_CSFS_MSG = CSFS_MSG + random .randbytes (random .randrange (1 , extendable_length ))
1274
+ OTHER_CSFS_MSG = CSFS_MSG
1275
+
1276
+ while OTHER_CSFS_MSG == CSFS_MSG :
1277
+ OTHER_CSFS_MSG = random .randbytes (random .randrange (0 , 520 ))
1278
+
1279
+ UNK_PUBKEY = random .randbytes (random .randrange (1 , 520 ))
1280
+ while len (UNK_PUBKEY ) == 32 :
1281
+ UNK_PUBKEY = random .randbytes (random .randrange (1 , 520 ))
1282
+
1283
+ # Sigops ratio test is included elsewhere to mix and match with other sigops
1284
+ scripts = [
1285
+ ("simple_csfs" , CScript ([CSFS_MSG , pubs [0 ], OP_CHECKSIGFROMSTACK , OP_1 , OP_EQUAL ])),
1286
+ ("simple_fail_csfs" , CScript ([CSFS_MSG , pubs [0 ], OP_CHECKSIGFROMSTACK , OP_0 , OP_EQUAL ])),
1287
+ ("unk_pubkey_csfs" , CScript ([CSFS_MSG , UNK_PUBKEY , OP_CHECKSIGFROMSTACK ])),
1288
+ ("onearg_csfs" , CScript ([pubs [0 ], OP_CHECKSIGFROMSTACK ])),
1289
+ ("twoargs_csfs" , CScript ([CSFS_MSG , pubs [0 ], OP_CHECKSIGFROMSTACK ])),
1290
+ ("empty_pk_csfs" , CScript ([CSFS_MSG , OP_0 , OP_CHECKSIGFROMSTACK , OP_0 , OP_EQUAL ])),
1291
+ ]
1292
+
1293
+ tap = taproot_construct (pubs [0 ], scripts )
1294
+
1295
+ spenders = []
1296
+
1297
+ # "sighash" is actually the bip340 message being directly verified against
1298
+ add_spender (spenders , comment = "bip348_csfs/simple" , tap = tap , leaf = "simple_csfs" , key = secs [0 ], inputs = [getter ("sign" )], sighash = CSFS_MSG , failure = {"sighash" : OTHER_CSFS_MSG }, ** ERR_SCHNORR_SIG )
1299
+ if TRUNC_CSFS_MSG is not None :
1300
+ add_spender (spenders , comment = "bip348_csfs/trunc_msg" , tap = tap , leaf = "onearg_csfs" , key = secs [0 ], inputs = [getter ("sign" ), CSFS_MSG ], standard = len (CSFS_MSG )<= 80 , sighash = CSFS_MSG , failure = {"inputs" : [getter ("sign" ), TRUNC_CSFS_MSG ]}, ** ERR_SCHNORR_SIG )
1301
+ if EXTEND_CSFS_MSG is not None :
1302
+ add_spender (spenders , comment = "bip348_csfs/extend_msg" , tap = tap , leaf = "onearg_csfs" , key = secs [0 ], inputs = [getter ("sign" ), CSFS_MSG ], standard = len (CSFS_MSG )<= 80 , sighash = CSFS_MSG , failure = {"inputs" : [getter ("sign" ), EXTEND_CSFS_MSG ]}, ** ERR_SCHNORR_SIG )
1303
+
1304
+ # Empty signature pushes zero onto stack and continues, unless the pubkey is empty
1305
+ add_spender (spenders , comment = "bip348_csfs/simple_fail" , tap = tap , leaf = "simple_fail_csfs" , inputs = [b'' ], failure = {"leaf" : "empty_pk_csfs" , "inputs" : [OTHER_CSFS_MSG ]}, ** ERR_PUBKEYTYPE )
1306
+
1307
+ # Unknown pubkey of non-zero size is unconditionally valid regardless of signature (but signature must exist)
1308
+ add_spender (spenders , comment = "bip348_csfs/unk_pubkey" , tap = tap , leaf = "unk_pubkey_csfs" , standard = False , key = secs [0 ], inputs = [getter ("sign" )], sighash = CSFS_MSG , failure = {"inputs" : []}, ** ERR_INVALID_STACK_OPERATION )
1309
+
1310
+ # You need three args for CSFS regardless of what is passed
1311
+ add_spender (spenders , comment = "bip348_csfs/onearg" , tap = tap , leaf = "onearg_csfs" , key = secs [0 ], inputs = [getter ("sign" ), CSFS_MSG ], standard = len (CSFS_MSG )<= 80 , sighash = CSFS_MSG , failure = {"inputs" : []}, ** ERR_INVALID_STACK_OPERATION )
1312
+ add_spender (spenders , comment = "bip348_csfs/twoarg" , tap = tap , leaf = "twoargs_csfs" , key = secs [0 ], inputs = [getter ("sign" )], sighash = CSFS_MSG , failure = {"inputs" : []}, ** ERR_INVALID_STACK_OPERATION )
1313
+
1314
+ # If a known pubkey's signature is not 64 bytes or empty it MUST fail immediately
1315
+ add_spender (spenders , comment = "bip348_csfs/simple_65_sig" , tap = tap , leaf = "simple_csfs" , key = secs [0 ], inputs = [getter ("sign" )], sighash = CSFS_MSG , failure = {"leaf" : "simple_fail_csfs" , "inputs" : [zero_appender (getter ("sign" ))]}, ** ERR_SCHNORR_SIG )
1316
+ add_spender (spenders , comment = "bip348_csfs/simple_63_sig" , tap = tap , leaf = "simple_csfs" , key = secs [0 ], inputs = [getter ("sign" )], sighash = CSFS_MSG , failure = {"leaf" : "simple_fail_csfs" , "inputs" : [byte_popper (getter ("sign" ))]}, ** ERR_SCHNORR_SIG )
1317
+
1318
+ return spenders
1319
+
1320
+ def sample_spenders ():
1321
+
1322
+ # Create key(s) for output creation, as well as key and script-spends
1323
+ secs = [generate_privkey () for _ in range (2 )]
1324
+ pubs = [compute_xonly_pubkey (sec )[0 ] for sec in secs ]
1325
+
1326
+ # Create a list of scripts which will be built into a taptree
1327
+ scripts = [
1328
+ # leaf label, followed by CScript
1329
+ ("encodeable_pushdata1" , CScript ([OP_DROP , OP_PUSHDATA1 , b'aa' * 75 ])),
1330
+ ("nonstd_encodeable_pushdata1" , CScript ([OP_PUSHDATA1 , b'aa' ])),
1331
+ ("dummyleaf" , CScript ([])),
1332
+ ]
1333
+
1334
+ # Build TaprootInfo using scripts and appropriate pubkey for output creation
1335
+ tap = taproot_construct (pubs [0 ], scripts )
1336
+
1337
+ # Finally, add spender(s).
1338
+ # Each spender embodies a test with an optional failure condition.
1339
+ # These failure conditions allow for fine-grained success/failure
1340
+ # conditions that are tested randomly.
1341
+ spenders = []
1342
+
1343
+ # Named comment, using first leaf from scripts, with empty string as witness data, no optional fail condition
1344
+ add_spender (spenders , comment = "tutorial/pushdata1" , tap = tap , leaf = "encodeable_pushdata1" , inputs = [b'\x00 ' ], no_fail = True )
1345
+
1346
+ # Spender with alternative failure tapscript via over-riding "failure" dictionary, along with the failure's expected err_msg / ERR_*
1347
+ add_spender (spenders , comment = "tutorial/pushdata1redux" , tap = tap , leaf = "encodeable_pushdata1" , inputs = [b'\x00 ' ], failure = {"leaf" : "dummyleaf" }, ** ERR_EVAL_FALSE )
1348
+
1349
+ # Spender that is non-standard but otherwise valid, with extraneous signature data from inner key for optional failure condition
1350
+ add_spender (spenders , comment = "tutorial/nonminpushdata1" , tap = tap , leaf = "nonstd_encodeable_pushdata1" , key = secs [0 ], standard = False , failure = {"inputs" : [getter ("sign" )]}, ** ERR_CLEANSTACK )
1351
+
1352
+ # New scripts=[] can be defined, and rinse-repeated as necessary until the spenders list is returned for execution
1353
+ return spenders
1354
+
1240
1355
# Consensus validation flags to use in dumps for tests with "legacy/" or "inactive/" prefix.
1241
1356
LEGACY_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY"
1242
1357
# Consensus validation flags to use in dumps for all other tests.
@@ -1765,7 +1880,15 @@ def run_test(self):
1765
1880
self .gen_test_vectors ()
1766
1881
1767
1882
self .log .info ("Post-activation tests..." )
1768
- self .test_spenders (self .nodes [0 ], spenders_taproot_active (), input_counts = [1 , 2 , 2 , 2 , 2 , 3 ])
1883
+
1884
+ # New sub-tests not checking standardness can be added to consensus_spenders
1885
+ # to allow for increased coverage across input types.
1886
+ # See sample_spenders for a minimal example
1887
+ consensus_spenders = sample_spenders ()
1888
+ consensus_spenders += bip348_csfs_spenders ()
1889
+ consensus_spenders += spenders_taproot_active ()
1890
+ self .test_spenders (self .nodes [0 ], consensus_spenders , input_counts = [1 , 2 , 2 , 2 , 2 , 3 ])
1891
+
1769
1892
# Run each test twice; once in isolation, and once combined with others. Testing in isolation
1770
1893
# means that the standardness is verified in every test (as combined transactions are only standard
1771
1894
# when all their inputs are standard).
0 commit comments