Browse Source
Initial support for (a and b), (a or b), and 2-of-3 escrow transactions (where a, b, and c are keys).0.8
Gavin Andresen
13 years ago
5 changed files with 575 additions and 88 deletions
@ -0,0 +1,288 @@ |
|||||||
|
#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 <boost/tuple/tuple.hpp> |
||||||
|
|
||||||
|
#include <openssl/ec.h> |
||||||
|
#include <openssl/err.h> |
||||||
|
|
||||||
|
#include "keystore.h" |
||||||
|
#include "main.h" |
||||||
|
#include "script.h" |
||||||
|
#include "wallet.h" |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
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); |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(multisig_tests) |
||||||
|
|
||||||
|
CScript |
||||||
|
sign_multisig(CScript scriptPubKey, vector<CKey> keys, CTransaction transaction, int whichIn) |
||||||
|
{ |
||||||
|
uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL); |
||||||
|
|
||||||
|
CScript result; |
||||||
|
result << OP_0; // CHECKMULTISIG bug workaround
|
||||||
|
BOOST_FOREACH(CKey key, keys) |
||||||
|
{ |
||||||
|
vector<unsigned char> vchSig; |
||||||
|
BOOST_CHECK(key.Sign(hash, vchSig)); |
||||||
|
vchSig.push_back((unsigned char)SIGHASH_ALL); |
||||||
|
result << vchSig; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(multisig_verify) |
||||||
|
{ |
||||||
|
CKey key[4]; |
||||||
|
for (int i = 0; i < 4; i++) |
||||||
|
key[i].MakeNewKey(); |
||||||
|
|
||||||
|
CScript a_and_b; |
||||||
|
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
CScript a_or_b; |
||||||
|
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
CScript escrow; |
||||||
|
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
CTransaction 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; |
||||||
|
|
||||||
|
CTransaction 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; |
||||||
|
} |
||||||
|
|
||||||
|
vector<CKey> keys; |
||||||
|
CScript s; |
||||||
|
|
||||||
|
// 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)); |
||||||
|
|
||||||
|
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)); |
||||||
|
|
||||||
|
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)); |
||||||
|
} |
||||||
|
|
||||||
|
// Test a OR b:
|
||||||
|
for (int i = 0; i < 4; i++) |
||||||
|
{ |
||||||
|
keys.clear(); |
||||||
|
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)); |
||||||
|
else |
||||||
|
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i)); |
||||||
|
} |
||||||
|
s.clear(); |
||||||
|
s << OP_0 << OP_0; |
||||||
|
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0)); |
||||||
|
s.clear(); |
||||||
|
s << OP_0 << OP_1; |
||||||
|
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0)); |
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) |
||||||
|
for (int j = 0; j < 4; j++) |
||||||
|
{ |
||||||
|
keys.clear(); |
||||||
|
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)); |
||||||
|
else |
||||||
|
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 2: %d %d", i, j)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(multisig_IsStandard) |
||||||
|
{ |
||||||
|
CKey key[3]; |
||||||
|
for (int i = 0; i < 3; i++) |
||||||
|
key[i].MakeNewKey(); |
||||||
|
|
||||||
|
CScript a_and_b; |
||||||
|
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(::IsStandard(a_and_b)); |
||||||
|
|
||||||
|
CScript a_or_b; |
||||||
|
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(::IsStandard(a_or_b)); |
||||||
|
|
||||||
|
CScript escrow; |
||||||
|
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(::IsStandard(escrow)); |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(multisig_Solver1) |
||||||
|
{ |
||||||
|
// Tests Solver() that returns lists of keys that are
|
||||||
|
// required to satisfy a ScriptPubKey
|
||||||
|
//
|
||||||
|
// Also tests IsMine() and ExtractAddress()
|
||||||
|
//
|
||||||
|
// Note: ExtractAddress 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; |
||||||
|
CKey key[3]; |
||||||
|
CBitcoinAddress keyaddr[3]; |
||||||
|
for (int i = 0; i < 3; i++) |
||||||
|
{ |
||||||
|
key[i].MakeNewKey(); |
||||||
|
keystore.AddKey(key[i]); |
||||||
|
keyaddr[i].SetPubKey(key[i].GetPubKey()); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
vector<vector<pair<opcodetype, valtype> > > solutions; |
||||||
|
CScript s; |
||||||
|
s << key[0].GetPubKey() << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(Solver(s, 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]); |
||||||
|
BOOST_CHECK(IsMine(keystore, s)); |
||||||
|
BOOST_CHECK(!IsMine(emptykeystore, s)); |
||||||
|
} |
||||||
|
{ |
||||||
|
vector<vector<pair<opcodetype, valtype> > > solutions; |
||||||
|
CScript s; |
||||||
|
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(Solver(s, 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]); |
||||||
|
BOOST_CHECK(IsMine(keystore, s)); |
||||||
|
BOOST_CHECK(!IsMine(emptykeystore, s)); |
||||||
|
} |
||||||
|
{ |
||||||
|
vector<vector<pair<opcodetype, valtype> > > solutions; |
||||||
|
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); |
||||||
|
CBitcoinAddress addr; |
||||||
|
BOOST_CHECK(!ExtractAddress(s, &keystore, addr)); |
||||||
|
BOOST_CHECK(IsMine(keystore, s)); |
||||||
|
BOOST_CHECK(!IsMine(emptykeystore, s)); |
||||||
|
} |
||||||
|
{ |
||||||
|
vector<vector<pair<opcodetype, valtype> > > solutions; |
||||||
|
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(IsMine(keystore, s)); |
||||||
|
BOOST_CHECK(!IsMine(emptykeystore, s)); |
||||||
|
} |
||||||
|
{ |
||||||
|
vector<vector<pair<opcodetype, valtype> > > solutions; |
||||||
|
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_AUTO_TEST_CASE(multisig_Sign) |
||||||
|
{ |
||||||
|
// 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]); |
||||||
|
} |
||||||
|
|
||||||
|
CScript a_and_b; |
||||||
|
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
CScript a_or_b; |
||||||
|
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
CScript escrow; |
||||||
|
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
CTransaction 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; |
||||||
|
|
||||||
|
CTransaction 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; |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) |
||||||
|
{ |
||||||
|
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END() |
Loading…
Reference in new issue