OP_EVAL implementation

OP_EVAL is a new opcode that evaluates an item on the stack as a script.
It enables a new type of bitcoin address that needs an arbitrarily
complex script to redeem.
This commit is contained in:
Gavin Andresen
2011-10-03 13:05:43 -04:00
parent cc40ba2151
commit e679ec969c
15 changed files with 1165 additions and 582 deletions

View File

@@ -20,9 +20,7 @@ using namespace boost::assign;
typedef vector<unsigned char> valtype;
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, int nHashType);
BOOST_AUTO_TEST_SUITE(multisig_tests)
@@ -76,24 +74,25 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
vector<CKey> keys;
CScript s;
int nUnused = 0;
// Test a AND b:
keys.clear();
keys += key[0],key[1]; // magic operator+= from boost.assign
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, 0));
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0));
for (int i = 0; i < 4; i++)
{
keys.clear();
keys += key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, 0), strprintf("a&b 1: %d", i));
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 1: %d", i));
keys.clear();
keys += key[1],key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, 0), strprintf("a&b 2: %d", i));
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 2: %d", i));
}
// Test a OR b:
@@ -103,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys += key[i];
s = sign_multisig(a_or_b, keys, txTo[1], 0);
if (i == 0 || i == 1)
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
else
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
}
s.clear();
s << OP_0 << OP_0;
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
s.clear();
s << OP_0 << OP_1;
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
for (int i = 0; i < 4; i++)
@@ -122,16 +121,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys += key[i],key[j];
s = sign_multisig(escrow, keys, txTo[2], 0);
if (i < j && i < 3 && j < 3)
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 1: %d %d", i, j));
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 1: %d %d", i, j));
else
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 2: %d %d", i, j));
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 2: %d %d", i, j));
}
}
BOOST_AUTO_TEST_CASE(multisig_IsStandard)
{
CKey key[3];
for (int i = 0; i < 3; i++)
CKey key[4];
for (int i = 0; i < 4; i++)
key[i].MakeNewKey();
CScript a_and_b;
@@ -145,6 +144,21 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(::IsStandard(escrow));
CScript one_of_four;
one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG;
BOOST_CHECK(!::IsStandard(one_of_four));
CScript malformed[6];
malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
malformed[1] << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
malformed[2] << OP_0 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
malformed[3] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_0 << OP_CHECKMULTISIG;
malformed[4] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_CHECKMULTISIG;
malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey();
for (int i = 0; i < 6; i++)
BOOST_CHECK(!::IsStandard(malformed[i]));
}
BOOST_AUTO_TEST_CASE(multisig_Solver1)
@@ -170,13 +184,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << key[0].GetPubKey() << OP_CHECKSIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
@@ -184,13 +197,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
@@ -198,47 +210,40 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 2);
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4);
CBitcoinAddress addr;
BOOST_CHECK(!ExtractAddress(s, &keystore, addr));
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(solutions.size() == 2);
if (solutions.size() == 2)
{
BOOST_CHECK(solutions[0].size() == 1);
BOOST_CHECK(solutions[1].size() == 1);
}
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4);
vector<CBitcoinAddress> addrs;
int nRequired;
BOOST_CHECK(ExtractAddresses(s, &keystore, whichType, addrs, nRequired));
BOOST_CHECK(addrs[0] == keyaddr[0]);
BOOST_CHECK(addrs[1] == keyaddr[1]);
BOOST_CHECK(nRequired = 1);
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(solutions.size() == 3);
if (solutions.size() == 3)
{
BOOST_CHECK(solutions[0].size() == 2);
BOOST_CHECK(solutions[1].size() == 2);
BOOST_CHECK(solutions[2].size() == 2);
}
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 5);
}
}

View File

@@ -0,0 +1,203 @@
#include <boost/assert.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
#include "../main.h"
#include "../script.h"
#include "../wallet.h"
using namespace std;
// Test routines internal to script.cpp:
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType);
BOOST_AUTO_TEST_SUITE(script_op_eval_tests)
BOOST_AUTO_TEST_CASE(script_op_eval1)
{
// OP_EVAL looks like this:
// scriptSig: <sig> <sig...> <serialized_script>
// scriptPubKey: DUP HASH160 <hash> EQUALVERIFY EVAL
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
CBasicKeyStore keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey();
keystore.AddKey(key[i]);
}
// 8 Scripts: checking all combinations of
// different keys, straight/EVAL, pubkey/pubkeyhash
CScript standardScripts[4];
standardScripts[0] << key[0].GetPubKey() << OP_CHECKSIG;
standardScripts[1].SetBitcoinAddress(key[1].GetPubKey());
standardScripts[2] << key[1].GetPubKey() << OP_CHECKSIG;
standardScripts[3].SetBitcoinAddress(key[2].GetPubKey());
CScript evalScripts[4];
uint160 sigScriptHashes[4];
for (int i = 0; i < 4; i++)
{
sigScriptHashes[i] = Hash160(standardScripts[i]);
keystore.AddCScript(sigScriptHashes[i], standardScripts[i]);
evalScripts[i] << OP_DUP << OP_HASH160 << sigScriptHashes[i] << OP_EQUALVERIFY << OP_EVAL;
}
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(8);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = evalScripts[i];
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
}
BOOST_CHECK(txFrom.IsStandard());
CTransaction txTo[8]; // Spending transactions
for (int i = 0; i < 8; 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;
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
for (int i = 0; i < 8; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
}
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
int nUnused = 0;
bool sigOK = VerifySignature(txFrom, txTo[i], 0, nUnused);
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
txTo[i].vin[0].scriptSig = sigSave;
}
}
BOOST_AUTO_TEST_CASE(script_op_eval2)
{
// Test OP_EVAL edge cases
CScript recurse;
recurse << OP_DUP << OP_EVAL;
uint160 recurseHash = Hash160(recurse);
CScript fund;
fund << OP_DUP << OP_HASH160 << recurseHash << OP_EQUALVERIFY << OP_EVAL;
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = fund;
BOOST_CHECK(txFrom.IsStandard()); // Looks like a standard transaction until you try to spend it
CTransaction 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 = CScript() << static_cast<std::vector<unsigned char> >(recurse);
txTo.vout[0].nValue = 1;
int nUnused = 0;
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0));
BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused));
}
BOOST_AUTO_TEST_CASE(script_op_eval3)
{
// Test the CScript::Set* methods
CBasicKeyStore keystore;
CKey key[4];
std::vector<CKey> keys;
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey();
keystore.AddKey(key[i]);
keys.push_back(key[i]);
}
CScript inner[4];
inner[0].SetBitcoinAddress(key[0].GetPubKey());
inner[1].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+2));
inner[2].SetMultisig(1, std::vector<CKey>(keys.begin(), keys.begin()+2));
inner[3].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+3));
CScript outer[4];
for (int i = 0; i < 4; i++)
{
outer[i].SetEval(inner[i]);
keystore.AddCScript(Hash160(inner[i]), inner[i]);
}
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(4);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = outer[i];
}
BOOST_CHECK(txFrom.IsStandard());
CTransaction 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;
txTo[i].vout[0].scriptPubKey = inner[i];
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
for (int i = 0; i < 4; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i));
}
}
BOOST_AUTO_TEST_CASE(script_op_eval_backcompat)
{
// Check backwards-incompatibility-testing code
CScript returnsEleven;
returnsEleven << OP_11;
// This will validate on new clients, but will
// be invalid on old clients (that interpret OP_EVAL as a no-op)
CScript fund;
fund << OP_EVAL << OP_11 << OP_EQUAL;
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = fund;
CTransaction 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 = CScript() << static_cast<std::vector<unsigned char> >(returnsEleven);
txTo.vout[0].nValue = 1;
int nUnused = 0;
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0));
BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -7,7 +7,7 @@
using namespace std;
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType);
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
BOOST_AUTO_TEST_SUITE(script_tests)
@@ -21,19 +21,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a };
static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a };
int nUnused = 0;
vector<vector<unsigned char> > directStack;
BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0));
BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0, nUnused));
vector<vector<unsigned char> > pushdata1Stack;
BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0));
BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0, nUnused));
BOOST_CHECK(pushdata1Stack == directStack);
vector<vector<unsigned char> > pushdata2Stack;
BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0));
BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0, nUnused));
BOOST_CHECK(pushdata2Stack == directStack);
vector<vector<unsigned char> > pushdata4Stack;
BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0));
BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0, nUnused));
BOOST_CHECK(pushdata4Stack == directStack);
}
@@ -71,6 +73,7 @@ sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction)
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
{
int nUnused = 0;
CKey key1, key2, key3;
key1.MakeNewKey();
key2.MakeNewKey();
@@ -91,19 +94,20 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
txTo12.vout[0].nValue = 1;
CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12);
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0));
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
txTo12.vout[0].nValue = 2;
BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0));
BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, 0));
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0));
CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, 0));
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
}
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
{
int nUnused = 0;
CKey key1, key2, key3, key4;
key1.MakeNewKey();
key2.MakeNewKey();
@@ -127,46 +131,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
std::vector<CKey> keys;
keys.push_back(key1); keys.push_back(key2);
CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key1); keys.push_back(key3);
CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key2); keys.push_back(key3);
CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear();
keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, nUnused, 0));
keys.clear(); // Must have signatures
CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, 0));
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, nUnused, 0));
}