/* * This file is part of the Flowee project * Copyright (C) 2011-2013 The Bitcoin Core developers * Copyright (C) 2019-2021 Tom Zander * * 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 . */ #include "multisig_tests.h" #include #include #include "keystore.h" #include "policy/policy.h" #include typedef std::vector valtype; CScript sign_multisig(CScript scriptPubKey, std::vector keys, const CTransaction &transaction, int whichIn) { uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, 0, SIGHASH_ALL); CScript result; result << OP_0; // CHECKMULTISIG bug workaround for (const PrivateKey &key : keys) { std::vector vchSig; const bool rc = key.signECDSA(hash, vchSig); assert(rc); vchSig.push_back((unsigned char)SIGHASH_ALL); result << vchSig; } return result; } void MultiSigTests::multisig_verify() { unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; PrivateKey key[4]; int64_t amount = 0; for (int i = 0; i < 4; i++) key[i].makeNewKey(true); CScript a_and_b; a_and_b << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; CScript a_or_b; a_or_b << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; CScript escrow; escrow << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << ToByteVector(key[2].getPubKey()) << OP_3 << OP_CHECKMULTISIG; CMutableTransaction txFrom; // Funding transaction txFrom.vout.resize(3); txFrom.vout[0].scriptPubKey = a_and_b; txFrom.vout[1].scriptPubKey = a_or_b; txFrom.vout[2].scriptPubKey = escrow; CMutableTransaction txTo[3]; // Spending transaction for (int i = 0; i < 3; 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; } std::vector keys; CScript s; Script::State state(flags); // Test a AND b: keys.assign(1,key[0]); keys.push_back(key[1]); s = sign_multisig(a_and_b, keys, txTo[0], 0); QVERIFY(Script::verify(s, a_and_b, MutableTransactionSignatureChecker(&txTo[0], 0, amount), state)); QVERIFY2(state.error == SCRIPT_ERR_OK, state.errorString()); for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); QVERIFY2(!Script::verify(s, a_and_b, MutableTransactionSignatureChecker(&txTo[0], 0, amount), state), strprintf("a&b 1: %d", i).c_str()); QVERIFY2(state.error == SCRIPT_ERR_INVALID_STACK_OPERATION, state.errorString()); keys.assign(1,key[1]); keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); QVERIFY2(!Script::verify(s, a_and_b, MutableTransactionSignatureChecker(&txTo[0], 0, amount), state), strprintf("a&b 2: %d", i).c_str()); QVERIFY2(state.error == SCRIPT_ERR_EVAL_FALSE, state.errorString()); } // Test a OR b: for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) { QVERIFY2(Script::verify(s, a_or_b, MutableTransactionSignatureChecker(&txTo[1], 0, amount), state), strprintf("a|b: %d", i).c_str()); QVERIFY2(state.error == SCRIPT_ERR_OK, state.errorString()); } else { QVERIFY2(!Script::verify(s, a_or_b, MutableTransactionSignatureChecker(&txTo[1], 0, amount), state), strprintf("a|b: %d", i).c_str()); QVERIFY2(state.error == SCRIPT_ERR_EVAL_FALSE, state.errorString()); } } s.clear(); s << OP_0 << OP_1; QVERIFY(!Script::verify(s, a_or_b, MutableTransactionSignatureChecker(&txTo[1], 0, amount), state)); QVERIFY2(state.error == SCRIPT_ERR_SIG_DER, state.errorString()); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { keys.assign(1,key[i]); keys.push_back(key[j]); s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) { QVERIFY2(Script::verify(s, escrow, MutableTransactionSignatureChecker(&txTo[2], 0, amount), state), strprintf("escrow 1: %d %d", i, j).c_str()); QVERIFY2(state.error == SCRIPT_ERR_OK, state.errorString()); } else { QVERIFY2(!Script::verify(s, escrow, MutableTransactionSignatureChecker(&txTo[2], 0, amount), state), strprintf("escrow 2: %d %d", i, j).c_str()); QVERIFY2(state.error == SCRIPT_ERR_EVAL_FALSE, state.errorString()); } } } void MultiSigTests::multisig_IsStandard() { PrivateKey key[4]; for (int i = 0; i < 4; i++) key[i].makeNewKey(true); Script::TxnOutType whichType; CScript a_and_b; a_and_b << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; int dataSize = 0; QVERIFY(::IsStandard(a_and_b, whichType, dataSize)); CScript a_or_b; a_or_b << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; QVERIFY(::IsStandard(a_or_b, whichType, dataSize)); CScript escrow; escrow << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << ToByteVector(key[2].getPubKey()) << OP_3 << OP_CHECKMULTISIG; QVERIFY(::IsStandard(escrow, whichType, dataSize)); CScript one_of_four; one_of_four << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << ToByteVector(key[2].getPubKey()) << ToByteVector(key[3].getPubKey()) << OP_4 << OP_CHECKMULTISIG; QVERIFY(!::IsStandard(one_of_four, whichType, dataSize)); CScript malformed[6]; malformed[0] << OP_3 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; malformed[1] << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_3 << OP_CHECKMULTISIG; malformed[2] << OP_0 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; malformed[3] << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_0 << OP_CHECKMULTISIG; malformed[4] << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_CHECKMULTISIG; malformed[5] << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()); for (int i = 0; i < 6; i++) { QVERIFY(::IsStandard(malformed[i], whichType, dataSize)); QCOMPARE(whichType, Script::TX_SCRIPT); } } void MultiSigTests::multisig_Solver1() { // Tests Solver() that returns lists of keys that are // required to satisfy a ScriptPubKey // // Also tests IsMine() and ExtractDestination() // // Note: ExtractDestination for the multisignature transactions // always returns false for this release, even if you have // one key that would satisfy an (a|b) or 2-of-3 keys needed // to spend an escrow transaction. // CBasicKeyStore keystore, emptykeystore, partialkeystore; PrivateKey key[3]; CTxDestination keyaddr[3]; for (int i = 0; i < 3; i++) { key[i].makeNewKey(true); keystore.AddKey(key[i]); keyaddr[i] = key[i].getPubKey().getKeyId(); } partialkeystore.AddKey(key[0]); { std::vector solutions; Script::TxnOutType whichType; CScript s; s << ToByteVector(key[0].getPubKey()) << OP_CHECKSIG; QVERIFY(Script::solver(s, whichType, solutions)); QVERIFY(solutions.size() == 1); CTxDestination addr; QVERIFY(ExtractDestination(s, addr)); QVERIFY(addr == keyaddr[0]); } { std::vector solutions; Script::TxnOutType whichType; CScript s; s << OP_DUP << OP_HASH160 << ToByteVector(key[0].getPubKey().getKeyId()) << OP_EQUALVERIFY << OP_CHECKSIG; QVERIFY(Script::solver(s, whichType, solutions)); QVERIFY(solutions.size() == 1); CTxDestination addr; QVERIFY(ExtractDestination(s, addr)); QVERIFY(addr == keyaddr[0]); } { std::vector solutions; Script::TxnOutType whichType; CScript s; s << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; QVERIFY(Script::solver(s, whichType, solutions)); QCOMPARE(solutions.size(), 4U); CTxDestination addr; QVERIFY(!ExtractDestination(s, addr)); } { std::vector solutions; Script::TxnOutType whichType; CScript s; s << OP_1 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << OP_2 << OP_CHECKMULTISIG; QVERIFY(Script::solver(s, whichType, solutions)); QCOMPARE(solutions.size(), 4U); std::vector addrs; int nRequired; QVERIFY(ExtractDestinations(s, whichType, addrs, nRequired)); QVERIFY(addrs[0] == keyaddr[0]); QVERIFY(addrs[1] == keyaddr[1]); QVERIFY(nRequired == 1); } { std::vector solutions; Script::TxnOutType whichType; CScript s; s << OP_2 << ToByteVector(key[0].getPubKey()) << ToByteVector(key[1].getPubKey()) << ToByteVector(key[2].getPubKey()) << OP_3 << OP_CHECKMULTISIG; QVERIFY(Script::solver(s, whichType, solutions)); QVERIFY(solutions.size() == 5); } }