@@ -979,4 +979,142 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
979979 }
980980}
981981
982+ /* * Sanity check the return value of SpendsNonAnchorWitnessProg for various output types. */
983+ /* */
984+ BOOST_AUTO_TEST_CASE (spends_witness_prog)
985+ {
986+ CCoinsView coins_dummy;
987+ CCoinsViewCache coins (&coins_dummy);
988+ CKey key;
989+ key.MakeNewKey (true );
990+ const CPubKey pubkey{key.GetPubKey ()};
991+ CMutableTransaction tx_create{}, tx_spend{};
992+ tx_create.vout .emplace_back (CConfidentialAsset (CAsset{}), CConfidentialValue (CAmount (0 )), CScript{});
993+ uint256 txid{};
994+ tx_spend.vin .emplace_back (txid, 0 );
995+ std::vector<std::vector<uint8_t >> sol_dummy;
996+
997+ // CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash,
998+ // WitnessV1Taproot, PayToAnchor, WitnessUnknown.
999+ static_assert (std::variant_size_v<CTxDestination> == 8 );
1000+
1001+ // Go through all defined output types and sanity check SpendsNonAnchorWitnessProg.
1002+
1003+ // P2PKH
1004+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (PKHash{pubkey});
1005+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::PUBKEYHASH);
1006+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1007+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1008+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1009+
1010+ // P2SH
1011+ auto redeem_script{CScript{} << OP_1 << OP_CHECKSIG};
1012+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash{redeem_script});
1013+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::SCRIPTHASH);
1014+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1015+ tx_spend.vin [0 ].scriptSig = CScript{} << OP_0 << ToByteVector (redeem_script);
1016+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1017+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1018+ tx_spend.vin [0 ].scriptSig .clear ();
1019+
1020+ // native P2WSH
1021+ const auto witness_script{CScript{} << OP_12 << OP_HASH160 << OP_DUP << OP_EQUAL};
1022+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (WitnessV0ScriptHash{witness_script});
1023+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::WITNESS_V0_SCRIPTHASH);
1024+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1025+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1026+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1027+
1028+ // P2SH-wrapped P2WSH
1029+ redeem_script = tx_create.vout [0 ].scriptPubKey ;
1030+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (redeem_script));
1031+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::SCRIPTHASH);
1032+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1033+ tx_spend.vin [0 ].scriptSig = CScript{} << ToByteVector (redeem_script);
1034+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1035+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1036+ tx_spend.vin [0 ].scriptSig .clear ();
1037+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1038+
1039+ // native P2WPKH
1040+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (WitnessV0KeyHash{pubkey});
1041+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::WITNESS_V0_KEYHASH);
1042+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1043+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1044+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1045+
1046+ // P2SH-wrapped P2WPKH
1047+ redeem_script = tx_create.vout [0 ].scriptPubKey ;
1048+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (redeem_script));
1049+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::SCRIPTHASH);
1050+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1051+ tx_spend.vin [0 ].scriptSig = CScript{} << ToByteVector (redeem_script);
1052+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1053+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1054+ tx_spend.vin [0 ].scriptSig .clear ();
1055+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1056+
1057+ // P2TR
1058+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (WitnessV1Taproot{XOnlyPubKey{pubkey}});
1059+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::WITNESS_V1_TAPROOT);
1060+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1061+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1062+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1063+
1064+ // P2SH-wrapped P2TR (undefined, non-standard)
1065+ redeem_script = tx_create.vout [0 ].scriptPubKey ;
1066+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (redeem_script));
1067+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::SCRIPTHASH);
1068+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1069+ tx_spend.vin [0 ].scriptSig = CScript{} << ToByteVector (redeem_script);
1070+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1071+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1072+ tx_spend.vin [0 ].scriptSig .clear ();
1073+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1074+
1075+ // Undefined version 1 witness program
1076+ WitnessUnknown wu = {1 , 2 , {}, CPubKey ()};
1077+ auto program1 = std::vector<unsigned char >{0x42 , 0x42 };
1078+ std::copy (program1.begin (), program1.end (), wu.program );
1079+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (wu);
1080+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::WITNESS_UNKNOWN);
1081+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1082+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1083+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1084+
1085+ // P2SH-wrapped undefined version 1 witness program
1086+ redeem_script = tx_create.vout [0 ].scriptPubKey ;
1087+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (redeem_script));
1088+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::SCRIPTHASH);
1089+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1090+ tx_spend.vin [0 ].scriptSig = CScript{} << ToByteVector (redeem_script);
1091+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1092+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1093+ tx_spend.vin [0 ].scriptSig .clear ();
1094+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1095+
1096+ // Various undefined version >1 32-byte witness programs.
1097+ const auto program{ToByteVector (XOnlyPubKey{pubkey})};
1098+ for (int i{2 }; i <= 16 ; ++i) {
1099+ wu = WitnessUnknown{static_cast <unsigned int >(i), static_cast <unsigned int >(program.size ()), {}, CPubKey ()};
1100+ std::copy (program.begin (), program.end (), wu.program );
1101+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (wu);
1102+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::WITNESS_UNKNOWN);
1103+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1104+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1105+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1106+
1107+ // It's also detected within P2SH.
1108+ redeem_script = tx_create.vout [0 ].scriptPubKey ;
1109+ tx_create.vout [0 ].scriptPubKey = GetScriptForDestination (ScriptHash (redeem_script));
1110+ BOOST_CHECK_EQUAL (Solver (tx_create.vout [0 ].scriptPubKey , sol_dummy), TxoutType::SCRIPTHASH);
1111+ tx_spend.vin [0 ].prevout .hash = tx_create.GetHash ();
1112+ tx_spend.vin [0 ].scriptSig = CScript{} << ToByteVector (redeem_script);
1113+ AddCoins (coins, CTransaction{tx_create}, 0 , false );
1114+ BOOST_CHECK (::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1115+ tx_spend.vin [0 ].scriptSig .clear ();
1116+ BOOST_CHECK (!::SpendsNonAnchorWitnessProg (CTransaction{tx_spend}, coins));
1117+ }
1118+ }
1119+
9821120BOOST_AUTO_TEST_SUITE_END ()
0 commit comments