@@ -45,6 +45,7 @@ constexpr int DERIVE_HARDENED = 16; // The final derivation is hardened, i.e. en
45
45
constexpr int MIXED_PUBKEYS = 32 ;
46
46
constexpr int XONLY_KEYS = 64 ; // X-only pubkeys are in use (and thus inferring/caching may swap parity of pubkeys/keyids)
47
47
constexpr int MISSING_PRIVKEYS = 128 ; // Not all private keys are available, so ToPrivateString will fail.
48
+ constexpr int SIGNABLE_FAILS = 256 ; // We can sign with this descriptor, but actually trying to sign will fail
48
49
49
50
/* * Compare two descriptors. If only one of them has a checksum, the checksum is ignored. */
50
51
bool EqualDescriptor (std::string a, std::string b)
@@ -126,8 +127,10 @@ std::set<std::pair<CPubKey, KeyOriginInfo>> GetKeyOriginData(const FlatSigningPr
126
127
return ret;
127
128
}
128
129
129
- void DoCheck (const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t >>& paths = ONLY_EMPTY,
130
- bool replace_apostrophe_with_h_in_prv=false , bool replace_apostrophe_with_h_in_pub=false )
130
+ void DoCheck (const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags,
131
+ const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type,
132
+ const std::set<std::vector<uint32_t >>& paths = ONLY_EMPTY, bool replace_apostrophe_with_h_in_prv=false ,
133
+ bool replace_apostrophe_with_h_in_pub=false , uint32_t spender_nlocktime=0 , uint32_t spender_nsequence=CTxIn::SEQUENCE_FINAL)
131
134
{
132
135
FlatSigningProvider keys_priv, keys_pub;
133
136
std::set<std::vector<uint32_t >> left_paths = paths;
@@ -303,16 +306,19 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
303
306
for (size_t n = 0 ; n < spks.size (); ++n) {
304
307
BOOST_CHECK_EQUAL (ref[n], HexStr (spks[n]));
305
308
306
- if (flags & SIGNABLE) {
309
+ if (flags & ( SIGNABLE | SIGNABLE_FAILS) ) {
307
310
CMutableTransaction spend;
311
+ spend.nLockTime = spender_nlocktime;
308
312
spend.vin .resize (1 );
313
+ spend.vin [0 ].nSequence = spender_nsequence;
309
314
spend.vout .resize (1 );
310
315
std::vector<CTxOut> utxos (1 );
311
316
PrecomputedTransactionData txdata;
312
317
txdata.Init (spend, std::move (utxos), /* force=*/ true );
313
318
MutableTransactionSignatureCreator creator{spend, 0 , CAmount{0 }, &txdata, SIGHASH_DEFAULT};
314
319
SignatureData sigdata;
315
- BOOST_CHECK_MESSAGE (ProduceSignature (FlatSigningProvider{keys_priv}.Merge (FlatSigningProvider{script_provider}), creator, spks[n], sigdata), prv);
320
+ const auto prod_sig_res = ProduceSignature (FlatSigningProvider{keys_priv}.Merge (FlatSigningProvider{script_provider}), creator, spks[n], sigdata);
321
+ BOOST_CHECK_MESSAGE (prod_sig_res == !(flags & SIGNABLE_FAILS), prv);
316
322
}
317
323
318
324
/* Infer a descriptor from the generated script, and verify its solvability and that it roundtrips. */
@@ -340,29 +346,40 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
340
346
BOOST_CHECK_MESSAGE (left_paths.empty (), " Not all expected key paths found: " + prv);
341
347
}
342
348
343
- void Check (const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t >>& paths = ONLY_EMPTY)
349
+ void Check (const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags,
350
+ const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type,
351
+ const std::set<std::vector<uint32_t >>& paths = ONLY_EMPTY, uint32_t spender_nlocktime=0 ,
352
+ uint32_t spender_nsequence=CTxIn::SEQUENCE_FINAL)
344
353
{
345
354
bool found_apostrophes_in_prv = false ;
346
355
bool found_apostrophes_in_pub = false ;
347
356
348
357
// Do not replace apostrophes with 'h' in prv and pub
349
- DoCheck (prv, pub, norm_pub, flags, scripts, type, paths);
358
+ DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv=*/ false ,
359
+ /* replace_apostrophe_with_h_in_pub=*/ false , /* spender_nlocktime=*/ spender_nlocktime,
360
+ /* spender_nsequence=*/ spender_nsequence);
350
361
351
362
// Replace apostrophes with 'h' in prv but not in pub, if apostrophes are found in prv
352
363
if (prv.find (' \' ' ) != std::string::npos) {
353
364
found_apostrophes_in_prv = true ;
354
- DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */ true , /* replace_apostrophe_with_h_in_pub = */ false );
365
+ DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv=*/ true ,
366
+ /* replace_apostrophe_with_h_in_pub=*/ false , /* spender_nlocktime=*/ spender_nlocktime,
367
+ /* spender_nsequence=*/ spender_nsequence);
355
368
}
356
369
357
370
// Replace apostrophes with 'h' in pub but not in prv, if apostrophes are found in pub
358
371
if (pub.find (' \' ' ) != std::string::npos) {
359
372
found_apostrophes_in_pub = true ;
360
- DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */ false , /* replace_apostrophe_with_h_in_pub = */ true );
373
+ DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv=*/ false ,
374
+ /* replace_apostrophe_with_h_in_pub=*/ true , /* spender_nlocktime=*/ spender_nlocktime,
375
+ /* spender_nsequence=*/ spender_nsequence);
361
376
}
362
377
363
378
// Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both
364
379
if (found_apostrophes_in_prv && found_apostrophes_in_pub) {
365
- DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */ true , /* replace_apostrophe_with_h_in_pub = */ true );
380
+ DoCheck (prv, pub, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv=*/ true ,
381
+ /* replace_apostrophe_with_h_in_pub=*/ true , /* spender_nlocktime=*/ spender_nlocktime,
382
+ /* spender_nsequence=*/ spender_nsequence);
366
383
}
367
384
}
368
385
@@ -529,11 +546,17 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
529
546
CheckUnparsable (" wsh(and_b(or_b(pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),s:pk(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))" , " wsh(and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))" , " and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)) is not sane: contains duplicate public keys" );
530
547
// Valid with extended keys.
531
548
Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", DEFAULT, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, {{},{0}});
532
- // Valid under sh(wsh()) and with a mix of xpubs and raw keys. Note how we can only satisfy a single
533
- // branch (timelock sat isn't implemented yet) but we can solve and sign the script nonetheless.
549
+ // Valid under sh(wsh()) and with a mix of xpubs and raw keys.
534
550
Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, {{},{0}});
535
551
// An exotic multisig, we can sign for both branches
536
552
Check (" wsh(thresh(1,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc),a:pkh(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))" , " wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))" , " wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))" , SIGNABLE, {{" 00204a4528fbc0947e02e921b54bd476fc8cc2ebb5c6ae2ccf10ed29fe2937fb6892" }}, OutputType::BECH32, {{},{0 }});
553
+ // We can sign for a script requiring the two kinds of timelock.
554
+ // But if we don't set a sequence high enough, we'll fail.
555
+ Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/1);
556
+ // And same for the nLockTime.
557
+ Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, {{},{0}}, /*spender_nlocktime=*/999, /*spender_nsequence=*/2);
558
+ // But if both are set to (at least) the required value, we'll succeed.
559
+ Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/2);
537
560
}
538
561
539
562
BOOST_AUTO_TEST_SUITE_END ()
0 commit comments