Browse Source
Initial support for (a and b), (a or b), and 2-of-3 escrow transactions (where a, b, and c are keys).miguelfreitas
Gavin Andresen
13 years ago
5 changed files with 575 additions and 88 deletions
@ -0,0 +1,288 @@
@@ -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