Files

345 lines
13 KiB
C++
Raw Permalink Normal View History

2017-11-09 19:34:51 +01:00
/*
* This file is part of the Flowee project
* Copyright (C) 2012-2015 The Bitcoin Core developers
2021-06-20 22:44:44 +02:00
* Copyright (C) 2018 Tom Zander <tom@flowee.org>
2017-11-09 19:34:51 +01:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2014-03-18 10:11:00 +01:00
2021-01-20 18:22:34 +01:00
#include <common/MutableTransactionSignatureChecker.h>
2018-12-28 12:10:28 +01:00
#include "script_P2SH_tests.h"
2013-04-13 00:13:08 -05:00
#include "keystore.h"
#include "main.h"
#include "policy/policy.h"
2026-05-07 13:50:12 +02:00
#include <hash.h>
2020-04-12 19:26:35 +02:00
#include <script/interpreter.h>
2014-08-29 22:07:39 +02:00
#include <utxo/UnspentOutputDatabase.h>
// Helpers:
static std::vector<unsigned char>
Serialize(const CScript& s)
{
2015-10-29 07:11:24 +01:00
std::vector<unsigned char> sSerialized(s.begin(), s.end());
return sSerialized;
}
static bool
2026-05-07 13:50:12 +02:00
VerifyWithFlags(const CScript& scriptSig, const CScript& scriptPubKey, uint32_t flags, ScriptError& err)
{
// Create dummy to/from transactions:
CMutableTransaction txFrom;
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = scriptPubKey;
CMutableTransaction txTo;
txTo.vin.resize(1);
txTo.vout.resize(1);
txTo.vin[0].prevout.n = 0;
txTo.vin[0].prevout.hash = txFrom.GetHash();
txTo.vin[0].scriptSig = scriptSig;
txTo.vout[0].nValue = 1;
2026-05-07 13:50:12 +02:00
Script::State state(flags);
2020-04-11 18:35:43 +02:00
bool ok = Script::verify(scriptSig, scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), state);
err = state.error;
return ok;
}
2026-05-07 13:50:12 +02:00
static bool
Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err)
{
return VerifyWithFlags(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, err);
}
2018-12-28 12:10:28 +01:00
void TestPaymentToScriptHash::norecurse()
{
ScriptError err;
// Make sure only the outer pay-to-script-hash does the
// extra-validation thing:
CScript invalidAsScript;
2021-03-06 14:30:20 +01:00
invalidAsScript << INVALIDOPCODE << INVALIDOPCODE;
CScript p2sh = GetScriptForDestination(CScriptID(invalidAsScript));
CScript scriptSig;
scriptSig << Serialize(invalidAsScript);
// Should not verify, because it will try to execute OP_INVALIDOPCODE
2018-12-28 12:10:28 +01:00
QVERIFY(!Verify(scriptSig, p2sh, true, err));
QCOMPARE(err, SCRIPT_ERR_BAD_OPCODE);
2012-07-26 00:48:39 +00:00
// Try to recur, and verification should succeed because
// the inner HASH160 <> EQUAL should only check the hash:
CScript p2sh2 = GetScriptForDestination(CScriptID(p2sh));
CScript scriptSig2;
scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh);
2018-12-28 12:10:28 +01:00
QVERIFY(Verify(scriptSig2, p2sh2, true, err));
QCOMPARE(err, SCRIPT_ERR_OK);
}
2018-12-28 12:10:28 +01:00
void TestPaymentToScriptHash::set()
{
2014-04-23 08:05:05 +02:00
LOCK(cs_main);
// Test the CScript::Set* methods
CBasicKeyStore keystore;
2022-07-06 22:12:33 +02:00
PrivateKey key[4];
2022-07-06 21:56:34 +02:00
std::vector<PublicKey> keys;
for (int i = 0; i < 4; i++)
{
2022-05-11 13:46:15 +02:00
key[i].makeNewKey(true);
keystore.AddKey(key[i]);
2022-05-11 13:46:15 +02:00
keys.push_back(key[i].getPubKey());
}
CScript inner[4];
2022-05-11 13:46:15 +02:00
inner[0] = GetScriptForDestination(key[0].getPubKey().getKeyId());
2022-07-06 21:56:34 +02:00
inner[1] = GetScriptForMultisig(2, std::vector<PublicKey>(keys.begin(), keys.begin()+2));
inner[2] = GetScriptForMultisig(1, std::vector<PublicKey>(keys.begin(), keys.begin()+2));
inner[3] = GetScriptForMultisig(2, std::vector<PublicKey>(keys.begin(), keys.begin()+3));
CScript outer[4];
for (int i = 0; i < 4; i++)
{
outer[i] = GetScriptForDestination(CScriptID(inner[i]));
keystore.AddCScript(inner[i]);
}
CMutableTransaction txFrom; // Funding transaction:
std::string reason;
txFrom.vout.resize(4);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = outer[i];
txFrom.vout[i].nValue = CENT;
}
2018-12-28 12:10:28 +01:00
QVERIFY(IsStandardTx(txFrom, reason));
CMutableTransaction txTo[4]; // Spending transactions
for (int i = 0; i < 4; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1*CENT;
txTo[i].vout[0].scriptPubKey = inner[i];
}
for (int i = 0; i < 4; i++)
{
2018-12-28 12:10:28 +01:00
QVERIFY(IsStandardTx(txTo[i], reason));
}
}
2018-12-28 12:10:28 +01:00
void TestPaymentToScriptHash::is()
{
// Test CScript::IsPayToScriptHash()
uint160 dummy;
CScript p2sh;
p2sh << OP_HASH160 << ToByteVector(dummy) << OP_EQUAL;
2018-12-28 12:10:28 +01:00
QVERIFY(p2sh.IsPayToScriptHash());
// Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes:
static const unsigned char direct[] = { OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
2018-12-28 12:10:28 +01:00
QVERIFY(CScript(direct, direct+sizeof(direct)).IsPayToScriptHash());
static const unsigned char pushdata1[] = { OP_HASH160, OP_PUSHDATA1, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
2018-12-28 12:10:28 +01:00
QVERIFY(!CScript(pushdata1, pushdata1+sizeof(pushdata1)).IsPayToScriptHash());
static const unsigned char pushdata2[] = { OP_HASH160, OP_PUSHDATA2, 20,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
2018-12-28 12:10:28 +01:00
QVERIFY(!CScript(pushdata2, pushdata2+sizeof(pushdata2)).IsPayToScriptHash());
static const unsigned char pushdata4[] = { OP_HASH160, OP_PUSHDATA4, 20,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL };
2018-12-28 12:10:28 +01:00
QVERIFY(!CScript(pushdata4, pushdata4+sizeof(pushdata4)).IsPayToScriptHash());
CScript not_p2sh;
2018-12-28 12:10:28 +01:00
QVERIFY(!not_p2sh.IsPayToScriptHash());
not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << ToByteVector(dummy) << OP_EQUAL;
2018-12-28 12:10:28 +01:00
QVERIFY(!not_p2sh.IsPayToScriptHash());
not_p2sh.clear(); not_p2sh << OP_NOP << ToByteVector(dummy) << OP_EQUAL;
2018-12-28 12:10:28 +01:00
QVERIFY(!not_p2sh.IsPayToScriptHash());
not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << OP_CHECKSIG;
2018-12-28 12:10:28 +01:00
QVERIFY(!not_p2sh.IsPayToScriptHash());
}
2026-05-07 13:50:12 +02:00
void TestPaymentToScriptHash::p2sh32()
{
const CScript trueRedeemScript = CScript() << OP_TRUE;
const CScript falseRedeemScript = CScript() << OP_FALSE;
const uint256 trueHash = Hash(trueRedeemScript.begin(), trueRedeemScript.end());
const uint256 falseHash = Hash(falseRedeemScript.begin(), falseRedeemScript.end());
CScript trueP2sh32;
trueP2sh32 << OP_HASH256 << ToByteVector(trueHash) << OP_EQUAL;
CScript falseP2sh32;
falseP2sh32 << OP_HASH256 << ToByteVector(falseHash) << OP_EQUAL;
2026-05-07 15:55:57 +02:00
QVERIFY(trueP2sh32.IsPayToScriptHash());
2026-05-07 13:50:12 +02:00
std::vector<unsigned char> zeros32(32, 0);
std::vector<unsigned char> direct32{OP_HASH256, 32};
direct32.insert(direct32.end(), zeros32.begin(), zeros32.end());
direct32.push_back(OP_EQUAL);
2026-05-07 15:55:57 +02:00
QVERIFY(CScript(direct32.begin(), direct32.end()).IsPayToScriptHash());
2026-05-07 13:50:12 +02:00
std::vector<unsigned char> pushdata1_32{OP_HASH256, OP_PUSHDATA1, 32};
pushdata1_32.insert(pushdata1_32.end(), zeros32.begin(), zeros32.end());
pushdata1_32.push_back(OP_EQUAL);
2026-05-07 15:55:57 +02:00
QVERIFY(!CScript(pushdata1_32.begin(), pushdata1_32.end()).IsPayToScriptHash());
2026-05-07 13:50:12 +02:00
std::vector<unsigned char> pushdata2_32{OP_HASH256, OP_PUSHDATA2, 32, 0};
pushdata2_32.insert(pushdata2_32.end(), zeros32.begin(), zeros32.end());
pushdata2_32.push_back(OP_EQUAL);
2026-05-07 15:55:57 +02:00
QVERIFY(!CScript(pushdata2_32.begin(), pushdata2_32.end()).IsPayToScriptHash());
2026-05-07 13:50:12 +02:00
std::vector<unsigned char> pushdata4_32{OP_HASH256, OP_PUSHDATA4, 32, 0, 0, 0};
pushdata4_32.insert(pushdata4_32.end(), zeros32.begin(), zeros32.end());
pushdata4_32.push_back(OP_EQUAL);
2026-05-07 15:55:57 +02:00
QVERIFY(!CScript(pushdata4_32.begin(), pushdata4_32.end()).IsPayToScriptHash());
2026-05-07 13:50:12 +02:00
std::vector<unsigned char> hashBytes;
2026-05-07 15:55:57 +02:00
QVERIFY(trueP2sh32.IsPayToScriptHash(&hashBytes));
2026-05-07 13:50:12 +02:00
QCOMPARE(hashBytes.size(), size_t(32));
QVERIFY(std::equal(hashBytes.begin(), hashBytes.end(), trueHash.begin()));
Script::TxnOutType typeRet = Script::TX_NONSTANDARD;
std::vector<std::vector<unsigned char> > solutions;
2026-05-07 15:55:57 +02:00
QVERIFY(Script::solver(trueP2sh32, typeRet, solutions));
2026-05-07 13:50:12 +02:00
QCOMPARE(typeRet, Script::TX_SCRIPTHASH);
QCOMPARE(solutions.size(), size_t(1));
QCOMPARE(solutions.front().size(), size_t(32));
int dataUsed = 0;
2026-05-07 15:55:57 +02:00
QVERIFY(IsStandard(trueP2sh32, typeRet, dataUsed));
2026-05-07 13:50:12 +02:00
CScript falseScriptSig;
falseScriptSig << Serialize(falseRedeemScript);
2026-05-07 15:55:57 +02:00
QVERIFY(Policy::isInputStandard(falseP2sh32, falseScriptSig));
2026-05-07 13:50:12 +02:00
ScriptError err;
QVERIFY(VerifyWithFlags(falseScriptSig, falseP2sh32, SCRIPT_VERIFY_P2SH, err));
QCOMPARE(err, SCRIPT_ERR_OK);
QVERIFY(!VerifyWithFlags(falseScriptSig, falseP2sh32, SCRIPT_VERIFY_P2SH | SCRIPT_ENABLE_P2SH_32, err));
QCOMPARE(err, SCRIPT_ERR_EVAL_FALSE);
}
2018-12-28 12:10:28 +01:00
void TestPaymentToScriptHash::switchover()
{
2012-07-26 00:48:39 +00:00
// Test switch over code
CScript notValid;
ScriptError err;
notValid << OP_11 << OP_12 << OP_EQUALVERIFY;
CScript scriptSig;
scriptSig << Serialize(notValid);
CScript fund = GetScriptForDestination(CScriptID(notValid));
// Validation should succeed under old rules (hash is correct):
2018-12-28 12:10:28 +01:00
QVERIFY(Verify(scriptSig, fund, false, err));
QCOMPARE(err, SCRIPT_ERR_OK);
// Fail under new:
2018-12-28 12:10:28 +01:00
QVERIFY(!Verify(scriptSig, fund, true, err));
QCOMPARE(err, SCRIPT_ERR_EQUALVERIFY);
}
2018-12-28 12:10:28 +01:00
void TestPaymentToScriptHash::AreInputsStandard()
{
2014-04-23 08:05:05 +02:00
LOCK(cs_main);
CBasicKeyStore keystore;
2022-07-06 22:12:33 +02:00
PrivateKey key[6];
2022-07-06 21:56:34 +02:00
std::vector<PublicKey> keys;
for (int i = 0; i < 6; i++)
{
2022-05-11 13:46:15 +02:00
key[i].makeNewKey(true);
keystore.AddKey(key[i]);
}
for (int i = 0; i < 3; i++)
2022-05-11 13:46:15 +02:00
keys.push_back(key[i].getPubKey());
CMutableTransaction txFrom;
txFrom.vout.resize(7);
// First three are standard:
2022-05-11 13:46:15 +02:00
CScript pay1 = GetScriptForDestination(key[0].getPubKey().getKeyId());
keystore.AddCScript(pay1);
CScript pay1of3 = GetScriptForMultisig(1, keys);
txFrom.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(pay1)); // P2SH (OP_CHECKSIG)
2012-07-01 18:54:00 +02:00
txFrom.vout[0].nValue = 1000;
txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG
2012-07-01 18:54:00 +02:00
txFrom.vout[1].nValue = 2000;
txFrom.vout[2].scriptPubKey = pay1of3; // ordinary OP_CHECKMULTISIG
2012-07-01 18:54:00 +02:00
txFrom.vout[2].nValue = 3000;
// vout[3] is complicated 1-of-3 AND 2-of-3
// ... that is OK if wrapped in P2SH:
CScript oneAndTwo;
2022-05-11 13:46:15 +02:00
oneAndTwo << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << ToByteVector(key[2].getPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIGVERIFY;
2022-05-11 13:46:15 +02:00
oneAndTwo << OP_2 << ToByteVector(key[3].getPubKey()) << ToByteVector(key[4].getPubKey()) << ToByteVector(key[5].getPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIG;
keystore.AddCScript(oneAndTwo);
txFrom.vout[3].scriptPubKey = GetScriptForDestination(CScriptID(oneAndTwo));
2012-07-01 18:54:00 +02:00
txFrom.vout[3].nValue = 4000;
2020-04-12 23:48:32 +02:00
// vout[4] is max sigchecks: Non-standard because its too long
CScript fifteenSigops; fifteenSigops << OP_1;
2020-04-12 23:48:32 +02:00
for (unsigned i = 0; i < Policy::MAX_SIGCHEKCS_PER_TX; i++)
2022-05-11 13:46:15 +02:00
fifteenSigops << ToByteVector(key[i%3].getPubKey());
fifteenSigops << OP_15 << OP_CHECKMULTISIG;
keystore.AddCScript(fifteenSigops);
txFrom.vout[4].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
2012-07-01 18:54:00 +02:00
txFrom.vout[4].nValue = 5000;
// vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
keystore.AddCScript(sixteenSigops);
txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
txFrom.vout[5].nValue = 5000;
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
keystore.AddCScript(twentySigops);
txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
txFrom.vout[6].nValue = 6000;
CMutableTransaction txTo;
txTo.vout.resize(1);
2022-05-11 13:46:15 +02:00
txTo.vout[0].scriptPubKey = GetScriptForDestination(key[1].getPubKey().getKeyId());
txTo.vin.resize(5);
for (int i = 0; i < 5; i++)
{
txTo.vin[i].prevout.n = i;
txTo.vin[i].prevout.hash = txFrom.GetHash();
}
2026-05-05 00:39:16 +02:00
// We're not testing validating signatures, so just create
// dummy signatures that DO include the correct P2SH scripts:
2026-05-05 00:39:16 +02:00
txTo.vin[0].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
txTo.vin[1].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
txTo.vin[2].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
for (size_t i = 0; i < txTo.vin.size(); ++i) {
const auto in = txTo.vin.at(i);
const auto prevOut = txFrom.vout.at(i);
2020-04-12 23:48:32 +02:00
const bool ok = Policy::isInputStandard(prevOut.scriptPubKey, in.scriptSig);
QCOMPARE(ok, i < 4);
}
}