From 23eb4a5bbe753adda0ff95c395bddbbfde695100 Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 24 Feb 2018 17:38:30 -0500 Subject: [PATCH 1/3] add unit tests for p2wsh and p2sh(p2wsh) --- src/base58.cpp | 4 ++- src/rpcmisc.cpp | 41 +++++++++++++++++++++++ src/script/standard.cpp | 48 +++++++++++++++++++++++---- src/script/standard.h | 45 +++++++++++++++++++++++--- src/test/base58_tests.cpp | 7 +++- src/test/script_P2SH_tests.cpp | 59 ++++++++++++++++++++++++++++++++-- src/wallet/wallet.cpp | 33 +++++++++++++++---- 7 files changed, 216 insertions(+), 21 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index 12e2496d..2c2913d2 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -216,6 +216,9 @@ public: bool operator()(const CKeyID& id) const { return addr->Set(id); } bool operator()(const CScriptID& id) const { return addr->Set(id); } + bool operator()(const WitnessV0ScriptHash& w) const { return false; } + bool operator()(const WitnessV0KeyHash& w) const { return false; } + bool operator()(const WitnessUnknown& w) const { return false; } bool operator()(const CNoDestination& no) const { return false; } }; @@ -386,4 +389,3 @@ libzcash::SpendingKey CZCSpendingKey::Get() const ss >> ret; return ret; } - diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index c12e0ccf..773f80df 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -145,6 +145,47 @@ public: } return obj; } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + obj.pushKV("isscript", false); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 0); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + //if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + //obj.pushKV("pubkey", HexStr(pubkey)); + //} + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.pushKV("isscript", true); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 0); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + //if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + //ProcessSubScript(subscript, obj); + //} + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", (int)id.version); + obj.pushKV("witness_program", HexStr(id.program, id.program + id.length)); + return obj; + } }; #endif diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 8d623895..83d56c04 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -26,13 +26,13 @@ const char* GetTxnOutputType(txnouttype t) { case TX_NONSTANDARD: return "nonstandard"; case TX_PUBKEY: return "pubkey"; - case TX_PUBKEY_REPLAY: return "pubkeyreplay"; case TX_PUBKEYHASH: return "pubkeyhash"; - case TX_PUBKEYHASH_REPLAY: return "pubkeyhashreplay"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; - case TX_MULTISIG_REPLAY: return "multisigreplay"; case TX_NULL_DATA: return "nulldata"; + case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; + case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; + case TX_WITNESS_UNKNOWN: return "witness_unknown"; } return NULL; } @@ -48,15 +48,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vectorclear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessUnknown& id) const + { + script->clear(); + *script << CScript::EncodeOP_N(id.version) << std::vector(id.program, id.program + id.length); + return true; + } }; } @@ -322,3 +340,21 @@ CScript GetScriptForMultisig(int nRequired, const std::vector& keys) script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script; } + +CScript GetScriptForWitness(const CScript& redeemscript) +{ + CScript ret; + + txnouttype typ; + std::vector > vSolutions; + if (Solver(redeemscript, typ, vSolutions)) { + if (typ == TX_PUBKEY) { + return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end()))); + } else if (typ == TX_PUBKEYHASH) { + return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); + } + } + uint256 hash; + CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin()); + return GetScriptForDestination(WitnessV0ScriptHash(hash)); +} diff --git a/src/script/standard.h b/src/script/standard.h index 6c8817ac..b2652161 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -63,13 +63,13 @@ enum txnouttype TX_NONSTANDARD, // 'standard' transaction types: TX_PUBKEY, - TX_PUBKEY_REPLAY, TX_PUBKEYHASH, - TX_PUBKEYHASH_REPLAY, TX_SCRIPTHASH, TX_MULTISIG, - TX_MULTISIG_REPLAY, TX_NULL_DATA, + TX_WITNESS_V0_SCRIPTHASH, + TX_WITNESS_V0_KEYHASH, + TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; class CNoDestination { @@ -78,6 +78,42 @@ public: friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; +struct WitnessV0ScriptHash : public uint256 +{ +WitnessV0ScriptHash() : uint256() {} + explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {} + using uint256::uint256; +}; + +struct WitnessV0KeyHash : public uint160 +{ +WitnessV0KeyHash() : uint160() {} + explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {} + using uint160::uint160; +}; + +//! CTxDestination subtype to encode any future Witness version +struct WitnessUnknown +{ + unsigned int version; + unsigned int length; + unsigned char program[40]; + + friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version != w2.version) return false; + if (w1.length != w2.length) return false; + return std::equal(w1.program, w1.program + w1.length, w2.program); + } + + friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version < w2.version) return true; + if (w1.version > w2.version) return false; + if (w1.length < w2.length) return true; + if (w1.length > w2.length) return false; + return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length); + } +}; + /** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set @@ -85,7 +121,7 @@ public: * * CScriptID: TX_SCRIPTHASH destination * A CTxDestination is the internal data type encoded in a CBitcoinAddress */ -typedef boost::variant CTxDestination; +typedef boost::variant CTxDestination; const char* GetTxnOutputType(txnouttype t); @@ -97,5 +133,6 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: CScript GetScriptForDestination(const CTxDestination& dest); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); +CScript GetScriptForWitness(const CScript& redeemscript); #endif // BITCOIN_SCRIPT_STANDARD_H diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 361d8b8e..0b619ade 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -92,6 +92,9 @@ public: { return (exp_addrType == "none"); } + + template + bool operator()(const X &none) const { return false; } }; // Visitor to check address payload @@ -115,6 +118,9 @@ public: { return exp_payload.size() == 0; } + + template + bool operator()(const X &none) { return false; } }; // Goal: check that parsed keys match test payload @@ -271,4 +277,3 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 812ed7f6..f7f2a2a0 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -44,7 +44,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); + return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); } @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(sign) txTo[i].vin[0].scriptSig = sigSave; } } - +/* BOOST_AUTO_TEST_CASE(segwitlock) { ScriptError err; @@ -158,6 +158,61 @@ BOOST_AUTO_TEST_CASE(segwitlock) BOOST_CHECK(!Verify(p2shp2wshsig, p2shp2wsh, true, err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err)); } +*/ + +BOOST_AUTO_TEST_CASE(segwitspend_wsh) +{ + ScriptError err; + CScript unwrappedPubKey,scriptSig; + unwrappedPubKey << OP_12 << OP_EQUAL; + scriptSig << OP_12; + + CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey)); + CScript p2shScriptSig = scriptSig; + p2shScriptSig << Serialize(unwrappedPubKey); + + CScript segwitPubKey = GetScriptForWitness(unwrappedPubKey); + CScript segwitScriptSig = scriptSig; + segwitScriptSig << Serialize(unwrappedPubKey); + + CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey)); + CScript p2shsegwitScriptSig = segwitScriptSig; + p2shsegwitScriptSig << Serialize(segwitPubKey); + + BOOST_CHECK(Verify(scriptSig, unwrappedPubKey, true, err)); + BOOST_CHECK(Verify(p2shScriptSig, p2shPubKey, true, err)); + + BOOST_CHECK(segwitPubKey.IsPayToWitnessScriptHash()); + + BOOST_CHECK(Verify(segwitScriptSig, segwitPubKey, true, err)); + BOOST_CHECK(Verify(p2shsegwitScriptSig, p2shsegwitPubKey, true, err)); +} + +/* +BOOST_AUTO_TEST_CASE(segwitspend_wpkh) +{ + ScriptError err; + CScript unwrappedPubKey,scriptSig; + unwrappedPubKey << OP_DUP << OP_HASH160 << pubkeyhash << OP_EQUALVERIFY << OP_CHECKSIG; + scriptSig << Sign() << pubkey; + + CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey)); + CScript p2shScriptSig = scriptSig; + p2shScriptSig << Serialize(unwrappedPubKey); + + CScript segwitPubKey = GetScriptForDestination(WitnessV0KeyHash(unwrappedPubKey)); + CScript segwitScriptSig = p2shScriptSig; + + CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey)); + CScript p2shsegwitScriptSig = segwitScriptSig; + p2shsegwitScriptSig << Serialize(segwitPubKey); + + BOOST_CHECK(Verify(scriptSig, unwrappedPubKey, true, err)); + BOOST_CHECK(Verify(p2shScriptSig, p2shPubKey, true, err)); + BOOST_CHECK(Verify(segwitScriptSig, segwitPubKey, true, err)); + BOOST_CHECK(Verify(p2shsegwitScriptSig, p2shsegwitpubkey, true, err)); +} +/**/ BOOST_AUTO_TEST_CASE(norecurse) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 01e21415..91cdba2f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -169,7 +169,7 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) { - + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; if (!fFileBacked) @@ -477,7 +477,7 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er } catch (const boost::filesystem::filesystem_error&) { // failure is ok (well, not really, but it's not worse than what we started with) } - + // try again if (!bitdb.Open(GetDataDir())) { // if it still fails, it probably means we can't even create the database env @@ -486,14 +486,14 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er return true; } } - + if (GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: if (!CWalletDB::Recover(bitdb, walletFile, true)) return false; } - + if (boost::filesystem::exists(GetDataDir() / walletFile)) { CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); @@ -507,7 +507,7 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er if (r == CDBEnv::RECOVER_FAIL) errorString += _("wallet.dat corrupt, salvage failed"); } - + return true; } @@ -2993,7 +2993,7 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) /** * Mark old keypool keys as used, - * and generate all new keys + * and generate all new keys */ bool CWallet::NewKeyPool() { @@ -3406,7 +3406,26 @@ public: Process(script); } - void operator()(const CNoDestination &none) {} + void operator()(const WitnessV0ScriptHash& scriptID) + { + CScriptID id; + CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin()); + CScript script; + if (keystore.GetCScript(id, script)) { + Process(script); + } + } + + void operator()(const WitnessV0KeyHash& keyid) + { + CKeyID id(keyid); + if (keystore.HaveKey(id)) { + vKeys.push_back(id); + } + } + + template + void operator()(const X &none) {} }; void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { From 8041223c76e42741d42a089e9a8f161af3caf3d4 Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 24 Feb 2018 19:18:16 -0500 Subject: [PATCH 2/3] add tests for SIGHASH_FORKID --- src/test/script_tests.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 0f3ebe05..2e2fdf2c 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -99,6 +99,17 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bo stream << tx2; BOOST_CHECK_MESSAGE(zcashconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); #endif + + // exercise the FORKID hashtype requirement + opcodetype op; + auto back = scriptPubKey.end() - 1; + if(scriptPubKey.GetOp(back, op) && + op >= OP_CHECKSIG && op <= OP_CHECKSIGVERIFY) { + if(expect && scriptPubKey.GetSigOpCount(scriptSig) > 0) { + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, flags | SCRIPT_VERIFY_FORKID, MutableTransactionSignatureChecker(&tx, 0), &err), "FORKID" + message); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_HASHTYPE, std::string(ScriptErrorString(err)) + ": " + message); + } + } } void static NegateSignatureS(std::vector& vchSig) { @@ -787,7 +798,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); -} +} BOOST_AUTO_TEST_CASE(script_combineSigs) { From 7ba99051c8bb59c09a1ce03571212d2bb79d1265 Mon Sep 17 00:00:00 2001 From: jc Date: Sun, 25 Feb 2018 07:38:39 -0500 Subject: [PATCH 3/3] add P2WPKH tests --- src/test/script_P2SH_tests.cpp | 170 +++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 74 deletions(-) diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index f7f2a2a0..9d803142 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -28,8 +28,7 @@ Serialize(const CScript& s) return sSerialized; } -static bool -Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err) +CMutableTransaction BuildTransaction(const CScript& scriptSig, const CScript& scriptPubKey) { // Create dummy to/from transactions: CMutableTransaction txFrom; @@ -44,6 +43,13 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; + return txTo; +} + +static bool +Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err) +{ + auto txTo = BuildTransaction(scriptSig, scriptPubKey); return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); } @@ -123,96 +129,112 @@ BOOST_AUTO_TEST_CASE(sign) txTo[i].vin[0].scriptSig = sigSave; } } -/* -BOOST_AUTO_TEST_CASE(segwitlock) -{ - ScriptError err; - - // any scriptSig will do here - CScript scriptSig; - scriptSig << ToByteVector(uint256()); - - uint160 hash20; - CScript p2wpkh; - p2wpkh << OP_0 << ToByteVector(hash20); - BOOST_CHECK(!Verify(scriptSig, p2wpkh, true, err)); - BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err)); - - CScript p2shp2wpkh = GetScriptForDestination(CScriptID(p2wpkh)); - CScript p2shp2wpkhsig = scriptSig; - p2shp2wpkhsig << Serialize(p2wpkh); - - BOOST_CHECK(!Verify(p2shp2wpkhsig, p2shp2wpkh, true, err)); - BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err)); - - uint256 hash32; - CScript p2wsh; - p2wsh << OP_0 << ToByteVector(hash32); - BOOST_CHECK(!Verify(scriptSig, p2wsh, true, err)); - BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err)); - - CScript p2shp2wsh = GetScriptForDestination(CScriptID(p2wsh)); - CScript p2shp2wshsig = scriptSig; - p2shp2wshsig << Serialize(p2wsh); - - BOOST_CHECK(!Verify(p2shp2wshsig, p2shp2wsh, true, err)); - BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err)); -} -*/ BOOST_AUTO_TEST_CASE(segwitspend_wsh) { - ScriptError err; - CScript unwrappedPubKey,scriptSig; - unwrappedPubKey << OP_12 << OP_EQUAL; - scriptSig << OP_12; + std::vector expects = {true, false}; - CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey)); - CScript p2shScriptSig = scriptSig; - p2shScriptSig << Serialize(unwrappedPubKey); + for(auto it = expects.begin(); it != expects.end(); it++) { + bool expect = *it; - CScript segwitPubKey = GetScriptForWitness(unwrappedPubKey); - CScript segwitScriptSig = scriptSig; - segwitScriptSig << Serialize(unwrappedPubKey); + ScriptError err; + CScript unwrappedPubKey,scriptSig; + unwrappedPubKey << OP_12 << OP_EQUAL; + if(expect) + scriptSig << OP_12; + else + scriptSig << OP_11; - CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey)); - CScript p2shsegwitScriptSig = segwitScriptSig; - p2shsegwitScriptSig << Serialize(segwitPubKey); + CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey)); + CScript p2shScriptSig = scriptSig; + p2shScriptSig << Serialize(unwrappedPubKey); - BOOST_CHECK(Verify(scriptSig, unwrappedPubKey, true, err)); - BOOST_CHECK(Verify(p2shScriptSig, p2shPubKey, true, err)); + CScript segwitPubKey = GetScriptForWitness(unwrappedPubKey); + CScript segwitScriptSig = scriptSig; + segwitScriptSig << Serialize(unwrappedPubKey); - BOOST_CHECK(segwitPubKey.IsPayToWitnessScriptHash()); + CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey)); + CScript p2shsegwitScriptSig = segwitScriptSig; + p2shsegwitScriptSig << Serialize(segwitPubKey); - BOOST_CHECK(Verify(segwitScriptSig, segwitPubKey, true, err)); - BOOST_CHECK(Verify(p2shsegwitScriptSig, p2shsegwitPubKey, true, err)); + BOOST_CHECK(Verify(scriptSig, unwrappedPubKey, true, err) == expect); + BOOST_CHECK(Verify(p2shScriptSig, p2shPubKey, true, err) == expect); + + BOOST_CHECK(p2shPubKey.IsPayToScriptHash()); + BOOST_CHECK(segwitPubKey.IsPayToWitnessScriptHash()); + BOOST_CHECK(p2shsegwitPubKey.IsPayToScriptHash()); + + BOOST_CHECK(Verify(segwitScriptSig, segwitPubKey, true, err) == expect); + BOOST_CHECK(Verify(p2shsegwitScriptSig, p2shsegwitPubKey, true, err) == expect); + } +} + +void BuildPKTransaction(const CScript& scriptPubKey, CKey signingKey, CKey pushKey, CScript additionalSig, bool expect, ScriptError_t expect_err, const CScript& scriptToSign) +{ + CMutableTransaction txTo = BuildTransaction(CScript(), scriptPubKey); + + auto lenR = 32; + auto lenS = 32; + + uint256 hash = SignatureHash(scriptToSign, txTo, 0, SIGHASH_ALL); + + std::vector vchSig; + signingKey.Sign(hash, vchSig, 0); + vchSig.push_back(static_cast(SIGHASH_ALL)); + + txTo.vin[0].scriptSig << vchSig << ToByteVector(pushKey.GetPubKey()); + txTo.vin[0].scriptSig += additionalSig; + + ScriptError err; + BOOST_CHECK(Verify(txTo.vin[0].scriptSig, scriptPubKey, true, err) == expect); + + if(!expect) + BOOST_CHECK_MESSAGE(err == expect_err, ScriptErrorString(err)); } -/* BOOST_AUTO_TEST_CASE(segwitspend_wpkh) { - ScriptError err; - CScript unwrappedPubKey,scriptSig; - unwrappedPubKey << OP_DUP << OP_HASH160 << pubkeyhash << OP_EQUALVERIFY << OP_CHECKSIG; - scriptSig << Sign() << pubkey; + std::vector expects = {true, false}; - CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey)); - CScript p2shScriptSig = scriptSig; - p2shScriptSig << Serialize(unwrappedPubKey); + CKey key1,key2; + key1.MakeNewKey(true); + key2.MakeNewKey(true); - CScript segwitPubKey = GetScriptForDestination(WitnessV0KeyHash(unwrappedPubKey)); - CScript segwitScriptSig = p2shScriptSig; + CScript unwrappedPubKey; + unwrappedPubKey << OP_DUP << OP_HASH160 << ToByteVector(key1.GetPubKey().GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey)); - CScript p2shsegwitScriptSig = segwitScriptSig; - p2shsegwitScriptSig << Serialize(segwitPubKey); + for(auto it = expects.begin(); it != expects.end(); it++) { + bool expect = *it; - BOOST_CHECK(Verify(scriptSig, unwrappedPubKey, true, err)); - BOOST_CHECK(Verify(p2shScriptSig, p2shPubKey, true, err)); - BOOST_CHECK(Verify(segwitScriptSig, segwitPubKey, true, err)); - BOOST_CHECK(Verify(p2shsegwitScriptSig, p2shsegwitpubkey, true, err)); + CKey k = expect ? key1 : key2; + + CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey)); + CScript segwitPubKey = GetScriptForWitness(unwrappedPubKey); + CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey)); + + BOOST_CHECK(p2shPubKey.IsPayToScriptHash()); + BOOST_CHECK(segwitPubKey.IsPayToWitnessPubKeyHash()); + BOOST_CHECK(p2shsegwitPubKey.IsPayToScriptHash()); + + CScript p2shScriptSig; + p2shScriptSig << Serialize(unwrappedPubKey); + CScript segwitScriptSig = CScript(); + CScript p2shsegwitScriptSig = segwitScriptSig; + p2shsegwitScriptSig << Serialize(segwitPubKey); + + BuildPKTransaction(unwrappedPubKey, key1, k, CScript(), expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey); + BuildPKTransaction(p2shPubKey, key1, k, p2shScriptSig, expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey); + BuildPKTransaction(segwitPubKey, key1, k, segwitScriptSig, expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey); + BuildPKTransaction(p2shsegwitPubKey, key1, k, p2shsegwitScriptSig, expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey); + + if(!expect) { + BuildPKTransaction(unwrappedPubKey, k, key1, CScript(), expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey); + BuildPKTransaction(p2shPubKey, k, key1, p2shScriptSig, expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey); + BuildPKTransaction(segwitPubKey, k, key1, segwitScriptSig, expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey); + BuildPKTransaction(p2shsegwitPubKey, k, key1, p2shsegwitScriptSig, expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey); + } + } } -/**/ BOOST_AUTO_TEST_CASE(norecurse) {