// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h"
case OP_ABS: if (bn < bnZero) bn = -bn; break;
case OP_NOT: bn = (bn == bnZero); break;
case OP_0NOTEQUAL: bn = (bn != bnZero); break;
+ default: assert(!"invalid opcode"); break;
}
popstack(stack);
stack.push_back(bn.getvch());
case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break;
case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break;
case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break;
+ default: assert(!"invalid opcode"); break;
}
popstack(stack);
popstack(stack);
-
-bool Solver(const CScript& scriptPubKey, vector<pair<opcodetype, valtype> >& vSolutionRet)
+//
+// Returns lists of public keys (or public key hashes), any one of which can
+// satisfy scriptPubKey
+//
+bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet)
{
// Templates
static vector<CScript> vTemplates;
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG);
+
+ // Sender provides two pubkeys, receivers provides two signatures
+ vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
+
+ // Sender provides two pubkeys, receivers provides one of two signatures
+ vTemplates.push_back(CScript() << OP_1 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
+
+ // Sender provides three pubkeys, receiver provides 2 of 3 signatures.
+ vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_PUBKEY << OP_3 << OP_CHECKMULTISIG);
}
// Scan templates
const CScript& script1 = scriptPubKey;
BOOST_FOREACH(const CScript& script2, vTemplates)
{
- vSolutionRet.clear();
+ vSolutionsRet.clear();
+
+ vector<pair<opcodetype, valtype> > currentSolution;
opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2;
{
if (pc1 == script1.end() && pc2 == script2.end())
{
- // Found a match
- reverse(vSolutionRet.begin(), vSolutionRet.end());
- return true;
+ return !vSolutionsRet.empty();
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
{
if (vch1.size() < 33 || vch1.size() > 120)
break;
- vSolutionRet.push_back(make_pair(opcode2, vch1));
+ currentSolution.push_back(make_pair(opcode2, vch1));
}
else if (opcode2 == OP_PUBKEYHASH)
{
if (vch1.size() != sizeof(uint160))
break;
- vSolutionRet.push_back(make_pair(opcode2, vch1));
+ currentSolution.push_back(make_pair(opcode2, vch1));
+ }
+ else if (opcode2 == OP_CHECKSIG)
+ {
+ vSolutionsRet.push_back(currentSolution);
+ currentSolution.clear();
+ }
+ else if (opcode2 == OP_CHECKMULTISIG)
+ { // Dig out the "m" from before the pubkeys:
+ CScript::const_iterator it = script2.begin();
+ opcodetype op_m;
+ script2.GetOp(it, op_m, vch1);
+ int m = CScript::DecodeOP_N(op_m);
+ int n = currentSolution.size();
+
+ if (m == 2 && n == 2)
+ {
+ vSolutionsRet.push_back(currentSolution);
+ currentSolution.clear();
+ }
+ else if (m == 1 && n == 2)
+ { // 2 solutions: either first key or second
+ for (int i = 0; i < 2; i++)
+ {
+ vector<pair<opcodetype, valtype> > s;
+ s.push_back(currentSolution[i]);
+ vSolutionsRet.push_back(s);
+ }
+ currentSolution.clear();
+ }
+ else if (m == 2 && n == 3)
+ { // 3 solutions: any pair
+ for (int i = 0; i < 2; i++)
+ for (int j = i+1; j < 3; j++)
+ {
+ vector<pair<opcodetype, valtype> > s;
+ s.push_back(currentSolution[i]);
+ s.push_back(currentSolution[j]);
+ vSolutionsRet.push_back(s);
+ }
+ currentSolution.clear();
+ }
}
else if (opcode1 != opcode2 || vch1 != vch2)
{
}
}
- vSolutionRet.clear();
+ vSolutionsRet.clear();
return false;
}
-bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
+bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
{
scriptSigRet.clear();
- vector<pair<opcodetype, valtype> > vSolution;
- if (!Solver(scriptPubKey, vSolution))
+ vector<vector<pair<opcodetype, valtype> > > vSolutions;
+ if (!Solver(scriptPubKey, vSolutions))
return false;
- // Compile solution
- CRITICAL_BLOCK(cs_mapKeys)
+ // See if we have all the keys for any of the solutions:
+ int whichSolution = -1;
+ for (int i = 0; i < vSolutions.size(); i++)
{
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+ int keysFound = 0;
+ CScript scriptSig;
+
+ BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
{
if (item.first == OP_PUBKEY)
{
- // Sign
const valtype& vchPubKey = item.second;
- if (!mapKeys.count(vchPubKey))
- return false;
- if (hash != 0)
+ CKey key;
+ vector<unsigned char> vchSig;
+ if (keystore.GetKey(Hash160(vchPubKey), key) && key.GetPubKey() == vchPubKey
+ && hash != 0 && key.Sign(hash, vchSig))
{
- vector<unsigned char> vchSig;
- if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig))
- return false;
vchSig.push_back((unsigned char)nHashType);
- scriptSigRet << vchSig;
+ scriptSig << vchSig;
+ ++keysFound;
}
}
else if (item.first == OP_PUBKEYHASH)
{
- // Sign and give pubkey
- map<uint160, valtype>::iterator mi = mapPubKeys.find(uint160(item.second));
- if (mi == mapPubKeys.end())
- return false;
- const vector<unsigned char>& vchPubKey = (*mi).second;
- if (!mapKeys.count(vchPubKey))
- return false;
- if (hash != 0)
+ CKey key;
+ vector<unsigned char> vchSig;
+ if (keystore.GetKey(uint160(item.second), key)
+ && hash != 0 && key.Sign(hash, vchSig))
{
- vector<unsigned char> vchSig;
- if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig))
- return false;
vchSig.push_back((unsigned char)nHashType);
- scriptSigRet << vchSig << vchPubKey;
+ scriptSig << vchSig << key.GetPubKey();
+ ++keysFound;
}
}
- else
- {
- return false;
- }
+ }
+ if (keysFound == vSolutions[i].size())
+ {
+ whichSolution = i;
+ scriptSigRet = scriptSig;
+ break;
}
}
+ if (whichSolution == -1)
+ return false;
+
+ // CHECKMULTISIG bug workaround:
+ if (vSolutions.size() != 1 ||
+ vSolutions[0].size() != 1)
+ {
+ scriptSigRet.insert(scriptSigRet.begin(), OP_0);
+ }
return true;
}
bool IsStandard(const CScript& scriptPubKey)
{
- vector<pair<opcodetype, valtype> > vSolution;
- return Solver(scriptPubKey, vSolution);
-}
-
-
-bool IsMine(const CScript& scriptPubKey)
-{
- CScript scriptSig;
- return Solver(scriptPubKey, 0, 0, scriptSig);
+ vector<vector<pair<opcodetype, valtype> > > vSolutions;
+ return Solver(scriptPubKey, vSolutions);
}
-bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector<unsigned char>& vchPubKeyRet)
+bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
- vchPubKeyRet.clear();
-
- vector<pair<opcodetype, valtype> > vSolution;
- if (!Solver(scriptPubKey, vSolution))
+ vector<vector<pair<opcodetype, valtype> > > vSolutions;
+ if (!Solver(scriptPubKey, vSolutions))
return false;
- CRITICAL_BLOCK(cs_mapKeys)
+ int keysFound = 0;
+ int keysRequired = 0;
+ for (int i = 0; i < vSolutions.size(); i++)
{
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+ BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
{
- valtype vchPubKey;
+ ++keysRequired;
if (item.first == OP_PUBKEY)
{
- vchPubKey = item.second;
+ const valtype& vchPubKey = item.second;
+ vector<unsigned char> vchPubKeyFound;
+ if (keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound) && vchPubKeyFound == vchPubKey)
+ ++keysFound;
}
else if (item.first == OP_PUBKEYHASH)
{
- map<uint160, valtype>::iterator mi = mapPubKeys.find(uint160(item.second));
- if (mi == mapPubKeys.end())
- continue;
- vchPubKey = (*mi).second;
- }
- if (!fMineOnly || mapKeys.count(vchPubKey))
- {
- vchPubKeyRet = vchPubKey;
- return true;
+ if (keystore.HaveKey(uint160(item.second)))
+ ++keysFound;
}
}
}
- return false;
-}
+ // Only consider transactions "mine" if we own ALL the
+ // keys involved. multi-signature transactions that are
+ // partially owned (somebody else has a key that can spend
+ // them) enable spend-out-from-under-you attacks, especially
+ // for shared-wallet situations.
+ return (keysFound == keysRequired);
+}
-bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret)
+bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
{
- hash160Ret = 0;
-
- vector<pair<opcodetype, valtype> > vSolution;
- if (!Solver(scriptPubKey, vSolution))
+ vector<vector<pair<opcodetype, valtype> > > vSolutions;
+ if (!Solver(scriptPubKey, vSolutions))
return false;
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+ for (int i = 0; i < vSolutions.size(); i++)
{
- if (item.first == OP_PUBKEYHASH)
- {
- hash160Ret = uint160(item.second);
+ if (vSolutions[i].size() != 1)
+ continue; // Can't return more than one address...
+
+ PAIRTYPE(opcodetype, valtype)& item = vSolutions[i][0];
+ if (item.first == OP_PUBKEY)
+ addressRet.SetPubKey(item.second);
+ else if (item.first == OP_PUBKEYHASH)
+ addressRet.SetHash160((uint160)item.second);
+ if (keystore == NULL || keystore->HaveKey(addressRet))
return true;
- }
}
return false;
}
+
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
vector<vector<unsigned char> > stack;
}
-bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq)
+bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq)
{
assert(nIn < txTo.vin.size());
CTxIn& txin = txTo.vin[nIn];
// The checksig op will also drop the signatures from its hash.
uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType);
- if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig))
+ if (!Solver(keystore, txout.scriptPubKey, hash, nHashType, txin.scriptSig))
return false;
txin.scriptSig = scriptPrereq + txin.scriptSig;
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType))
return false;
- // Anytime a signature is successfully verified, it's proof the outpoint is spent,
- // so lets update the wallet spent flag if it doesn't know due to wallet.dat being
- // restored from backup or the user making copies of wallet.dat.
- WalletUpdateSpent(txin.prevout);
-
return true;
}
+
+void CScript::SetMultisigAnd(const std::vector<CKey>& keys)
+{
+ assert(keys.size() >= 2);
+ this->clear();
+ *this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
+}
+void CScript::SetMultisigOr(const std::vector<CKey>& keys)
+{
+ assert(keys.size() >= 2);
+ this->clear();
+ *this << OP_1 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
+}
+void CScript::SetMultisigEscrow(const std::vector<CKey>& keys)
+{
+ assert(keys.size() >= 3);
+ this->clear();
+ *this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << keys[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
+}