OP_EVAL implementation
authorGavin Andresen <gavinandresen@gmail.com>
Mon, 3 Oct 2011 17:05:43 +0000 (13:05 -0400)
committerGavin Andresen <gavinandresen@gmail.com>
Mon, 19 Dec 2011 17:40:19 +0000 (12:40 -0500)
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.

15 files changed:
src/base58.h
src/bitcoinrpc.cpp
src/db.cpp
src/db.h
src/keystore.cpp
src/keystore.h
src/main.cpp
src/main.h
src/script.cpp
src/script.h
src/test/multisig_tests.cpp
src/test/script_op_eval_tests.cpp [new file with mode: 0644]
src/test/script_tests.cpp
src/wallet.cpp
src/wallet.h

index 113fa14..8fc546c 100644 (file)
@@ -268,6 +268,12 @@ public:
         SetHash160(Hash160(vchPubKey));
     }
 
+    bool SetScriptHash160(const uint160& hash160)
+    {
+        SetData(fTestNet ? 112 : 1, &hash160, 20);
+        return true;
+    }
+
     bool IsValid() const
     {
         int nExpectedSize = 20;
@@ -275,9 +281,20 @@ public:
         switch(nVersion)
         {
             case 0:
+                nExpectedSize = 20; // Hash of public key
+                fExpectTestNet = false;
+                break;
+            case 1:
+                nExpectedSize = 20; // OP_EVAL, hash of CScript
+                fExpectTestNet = false;
                 break;
 
             case 111:
+                nExpectedSize = 20;
+                fExpectTestNet = true;
+                break;
+            case 112:
+                nExpectedSize = 20;
                 fExpectTestNet = true;
                 break;
 
@@ -286,6 +303,14 @@ public:
         }
         return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
     }
+    bool IsScript() const
+    {
+        if (!IsValid())
+            return false;
+        if (fTestNet)
+            return nVersion == 112;
+        return nVersion == 1;
+    }
 
     CBitcoinAddress()
     {
index 122bf61..3e42e70 100644 (file)
@@ -667,7 +667,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
     if (params.size() > 1)
         nMinDepth = params[1].get_int();
 
-    // Get the set of pub keys that have the label
+    // Get the set of pub keys assigned to account
     string strAccount = AccountFromValue(params[0]);
     set<CBitcoinAddress> setAddress;
     GetAccountAddresses(strAccount, setAddress);
@@ -936,56 +936,30 @@ Value sendmany(const Array& params, bool fHelp)
     return wtx.GetHash().GetHex();
 }
 
-Value sendmultisig(const Array& params, bool fHelp)
+Value addmultisigaddress(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() < 4 || params.size() > 7)
+    if (fHelp || params.size() < 2 || params.size() > 3)
     {
-        string msg = "sendmultisig <fromaccount> <type> <[\"key\",\"key\"]> <amount> [minconf=1] [comment] [comment-to]\n"
-            "<type> is one of: \"and\", \"or\", \"escrow\"\n"
-            "<keys> is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n"
-            "<amount> is a real and is rounded to the nearest 0.00000001";
-        if (pwalletMain->IsCrypted())
-            msg += "\nrequires wallet passphrase to be set with walletpassphrase first";
+        string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
+            "Add a nrequired-to-sign multisignature address to the wallet\"\n"
+            "each key is a bitcoin address, hex or base58 public key\n"
+            "If [account] is specified, assign address to [account].";
         throw runtime_error(msg);
     }
 
-    string strAccount = AccountFromValue(params[0]);
-    string strType = params[1].get_str();
-    const Array& keys = params[2].get_array();
-    int64 nAmount = AmountFromValue(params[3]);
-    int nMinDepth = 1;
-    if (params.size() > 4)
-        nMinDepth = params[4].get_int();
-
-    CWalletTx wtx;
-    wtx.strFromAccount = strAccount;
-    if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
-        wtx.mapValue["comment"] = params[5].get_str();
-    if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty())
-        wtx.mapValue["to"]      = params[6].get_str();
-
-    if (pwalletMain->IsLocked())
-        throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
-
-    // Check funds
-    int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
-    if (nAmount > nBalance)
-        throw JSONRPCError(-6, "Account has insufficient funds");
+    int nRequired = params[0].get_int();
+    const Array& keys = params[1].get_array();
+    string strAccount;
+    if (params.size() > 2)
+        strAccount = AccountFromValue(params[2]);
 
     // Gather public keys
-    int nKeysNeeded = 0;
-    if (strType == "and" || strType == "or")
-        nKeysNeeded = 2;
-    else if (strType == "escrow")
-        nKeysNeeded = 3;
-    else
-        throw runtime_error("sendmultisig: <type> must be one of: and or and_or");
-    if (keys.size() != nKeysNeeded)
+    if (keys.size() < nRequired)
         throw runtime_error(
-            strprintf("sendmultisig: wrong number of keys (got %d, need %d)", keys.size(), nKeysNeeded));
+            strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired));
     std::vector<CKey> pubkeys;
-    pubkeys.resize(nKeysNeeded);
-    for (int i = 0; i < nKeysNeeded; i++)
+    pubkeys.resize(keys.size());
+    for (int i = 0; i < keys.size(); i++)
     {
         const std::string& ks = keys[i].get_str();
         if (ks.size() == 130) // hex public key
@@ -1003,32 +977,23 @@ Value sendmultisig(const Array& params, bool fHelp)
             CBitcoinAddress address(ks);
             if (!pwalletMain->GetKey(address, pubkeys[i]))
                 throw runtime_error(
-                    strprintf("sendmultisig: unknown address: %s",ks.c_str()));
+                    strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
         }
     }
 
-    // Send
-    CScript scriptPubKey;
-    if (strType == "and")
-        scriptPubKey.SetMultisigAnd(pubkeys);
-    else if (strType == "or")
-        scriptPubKey.SetMultisigOr(pubkeys);
-    else
-        scriptPubKey.SetMultisigEscrow(pubkeys);
+    // Construct using OP_EVAL
+    CScript inner;
+    inner.SetMultisig(nRequired, pubkeys);
 
-    CReserveKey keyChange(pwalletMain);
-    int64 nFeeRequired = 0;
-    bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired);
-    if (!fCreated)
-    {
-        if (nAmount + nFeeRequired > pwalletMain->GetBalance())
-            throw JSONRPCError(-6, "Insufficient funds");
-        throw JSONRPCError(-4, "Transaction creation failed");
-    }
-    if (!pwalletMain->CommitTransaction(wtx, keyChange))
-        throw JSONRPCError(-4, "Transaction commit failed");
+    uint160 scriptHash = Hash160(inner);
+    CScript scriptPubKey;
+    scriptPubKey.SetEval(inner);
+    pwalletMain->AddCScript(scriptHash, inner);
+    CBitcoinAddress address;
+    address.SetScriptHash160(scriptHash);
 
-    return wtx.GetHash().GetHex();
+    pwalletMain->SetAddressBookName(address, strAccount);
+    return address.ToString();
 }
 
 
@@ -1700,6 +1665,24 @@ Value validateaddress(const Array& params, bool fHelp)
             std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
             ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey)));
         }
+        else if (pwalletMain->HaveCScript(address.GetHash160()))
+        {
+            ret.push_back(Pair("isscript", true));
+            CScript subscript;
+            pwalletMain->GetCScript(address.GetHash160(), subscript);
+            ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript)));
+            std::vector<CBitcoinAddress> addresses;
+            txntype whichType;
+            int nRequired;
+            ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired);
+            ret.push_back(Pair("script", GetTxnTypeName(whichType)));
+            Array a;
+            BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
+                a.push_back(addr.ToString());
+            ret.push_back(Pair("addresses", a));
+            if (whichType == TX_MULTISIG)
+                ret.push_back(Pair("sigsrequired", nRequired));
+        }
         else
             ret.push_back(Pair("ismine", false));
         if (pwalletMain->mapAddressBook.count(address))
@@ -1946,7 +1929,7 @@ pair<string, rpcfn_type> pCallTable[] =
     make_pair("move",                   &movecmd),
     make_pair("sendfrom",               &sendfrom),
     make_pair("sendmany",               &sendmany),
-    make_pair("sendmultisig",           &sendmultisig),
+    make_pair("addmultisigaddress",     &addmultisigaddress),
     make_pair("gettransaction",         &gettransaction),
     make_pair("listtransactions",       &listtransactions),
     make_pair("signmessage",            &signmessage),
@@ -2590,16 +2573,15 @@ int CommandLineRPC(int argc, char *argv[])
             params[1] = v.get_obj();
         }
         if (strMethod == "sendmany"                && n > 2) ConvertTo<boost::int64_t>(params[2]);
-        if (strMethod == "sendmultisig"            && n > 2)
+        if (strMethod == "addmultisigaddress"      && n > 0) ConvertTo<boost::int64_t>(params[0]);
+        if (strMethod == "addmultisigaddress"      && n > 1)
         {
-            string s = params[2].get_str();
+            string s = params[1].get_str();
             Value v;
             if (!read_string(s, v) || v.type() != array_type)
-                throw runtime_error("sendmultisig: type mismatch "+s);
-            params[2] = v.get_array();
+                throw runtime_error("addmultisigaddress: type mismatch "+s);
+            params[1] = v.get_array();
         }
-        if (strMethod == "sendmultisig"            && n > 3) ConvertTo<double>(params[3]);
-        if (strMethod == "sendmultisig"            && n > 4) ConvertTo<boost::int64_t>(params[4]);
 
         // Execute
         Object reply = CallRPC(strMethod, params);
index 7195e06..52ad695 100644 (file)
@@ -934,6 +934,15 @@ int CWalletDB::LoadWallet(CWallet* pwallet)
                 if (nMinVersion > CLIENT_VERSION)
                     return DB_TOO_NEW;
             }
+            else if (strType == "cscript")
+            {
+                uint160 hash;
+                ssKey >> hash;
+                std::vector<unsigned char> script;
+                ssValue >> script;
+                if (!pwallet->LoadCScript(hash, script))
+                    return DB_CORRUPT;
+            }
         }
         pcursor->close();
     }
index 15bfb29..99dd88b 100644 (file)
--- a/src/db.h
+++ b/src/db.h
@@ -420,6 +420,18 @@ public:
         return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
     }
 
+    bool ReadCScript(const uint160 &hash, std::vector<unsigned char>& data)
+    {
+        data.clear();
+        return Read(std::make_pair(std::string("cscript"), hash), data);
+    }
+
+    bool WriteCScript(const uint160& hash, const std::vector<unsigned char>& data)
+    {
+        nWalletDBUpdated++;
+        return Write(std::make_pair(std::string("cscript"), hash), data, false);
+    }
+
     bool WriteBestBlock(const CBlockLocator& locator)
     {
         nWalletDBUpdated++;
index 6cf557f..c9b9b4a 100644 (file)
@@ -33,6 +33,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
     return true;
 }
 
+bool CBasicKeyStore::AddCScript(const uint160 &hash, const std::vector<unsigned char>& data)
+{
+    CRITICAL_BLOCK(cs_KeyStore)
+        mapData[hash] = data;
+    return true;
+}
+
+bool CBasicKeyStore::HaveCScript(const uint160& hash) const
+{
+    bool result;
+    CRITICAL_BLOCK(cs_KeyStore)
+        result = (mapData.count(hash) > 0);
+    return result;
+}
+
+
+bool CBasicKeyStore::GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const
+{
+    CRITICAL_BLOCK(cs_KeyStore)
+    {
+        DataMap::const_iterator mi = mapData.find(hash);
+        if (mi != mapData.end())
+        {
+            dataOut = (*mi).second;
+            return true;
+        }
+    }
+    return false;
+}
+
 bool CCryptoKeyStore::SetCrypted()
 {
     CRITICAL_BLOCK(cs_KeyStore)
index 3b91de6..942fb9a 100644 (file)
@@ -31,6 +31,10 @@ public:
     virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
     virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
 
+    virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data) =0;
+    virtual bool HaveCScript(const uint160 &hash) const =0;
+    virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const =0;
+
     // Generate a new key, and add it to the store
     virtual std::vector<unsigned char> GenerateNewKey();
     virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const
@@ -44,12 +48,14 @@ public:
 };
 
 typedef std::map<CBitcoinAddress, CSecret> KeyMap;
+typedef std::map<uint160, std::vector<unsigned char> > DataMap;
 
 // Basic key store, that keeps keys in an address->secret map
 class CBasicKeyStore : public CKeyStore
 {
 protected:
     KeyMap mapKeys;
+    DataMap mapData;
 
 public:
     bool AddKey(const CKey& key);
@@ -86,6 +92,9 @@ public:
         }
         return false;
     }
+    virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data);
+    virtual bool HaveCScript(const uint160 &hash) const;
+    virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const;
 };
 
 typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap;
index 052701e..0fb383f 100644 (file)
@@ -246,6 +246,65 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
     return ReadFromDisk(txdb, prevout, txindex);
 }
 
+bool CTransaction::IsStandard() const
+{
+    BOOST_FOREACH(const CTxIn& txin, vin)
+    {
+        // Biggest 'standard' txin is a 2-signature 2-of-3 escrow
+        // in an OP_EVAL, which is 2 ~80-byte signatures, 3
+        // ~65-byte public keys, plus a few script ops.
+        if (txin.scriptSig.size() > 400)
+            return error("nonstandard txin, size %d\n", txin.scriptSig.size());
+        if (!txin.scriptSig.IsPushOnly())
+            return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
+    }
+    BOOST_FOREACH(const CTxOut& txout, vout)
+        if (!::IsStandard(txout.scriptPubKey))
+            return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
+    return true;
+}
+
+//
+// Check transaction inputs, and make sure any
+// OP_EVAL transactions are evaluating IsStandard scripts
+//
+// Why bother? To avoid denial-of-service attacks; an attacker
+// can submit a standard DUP HASH... OP_EVAL transaction,
+// which will get accepted into blocks. The script being
+// EVAL'ed can be anything; an attacker could use a very
+// expensive-to-check-upon-redemption script like:
+//   DUP CHECKSIG DROP ... repeated 100 times... OP_1
+//
+bool CTransaction::IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const
+{
+    if (fTestNet)
+        return true; // Allow non-standard on testnet
+
+    for (int i = 0; i < vin.size(); i++)
+    {
+        COutPoint prevout = vin[i].prevout;
+        assert(mapInputs.count(prevout.hash) > 0);
+        CTransaction& txPrev = mapInputs[prevout.hash].second;
+
+        vector<vector<unsigned char> > vSolutions;
+        txntype whichType;
+        if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions))
+            return false;
+        if (whichType == TX_SCRIPTHASH)
+        {
+            vector<vector<unsigned char> > stack;
+            int nUnused;
+            if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, nUnused))
+                return false;
+            const vector<unsigned char>& subscript = stack.back();
+            if (!::IsStandard(CScript(subscript.begin(), subscript.end())))
+                return false;
+        }
+    }
+
+    return true;
+}
+
 
 
 int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
@@ -369,15 +428,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
     if ((int64)nLockTime > INT_MAX)
         return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
 
-    // Safety limits
-    unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
-    // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
-    // attacks disallow transactions with more than one SigOp per 34 bytes.
-    // 34 bytes because a TxOut is:
-    //   20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
-    if (GetSigOpCount() > nSize / 34 || nSize < 100)
-        return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
-
     // Rather not work on nonstandard transactions (unless -testnet)
     if (!fTestNet && !IsStandard())
         return error("AcceptToMemoryPool() : nonstandard transaction type");
@@ -421,15 +471,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
 
     if (fCheckInputs)
     {
-        // Check against previous transactions
+        map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
         map<uint256, CTxIndex> mapUnused;
+        if (!FetchInputs(txdb, mapUnused, false, false, mapInputs))
+        {
+            if (pfMissingInputs)
+                *pfMissingInputs = true;
+            return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
+        }
+
+        // Check for non-standard OP_EVALs in inputs
+        if (!IsStandardInputs(mapInputs))
+            return error("AcceptToMemoryPool() : nonstandard transaction input");
+
+        // Check against previous transactions
         int64 nFees = 0;
-        if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false))
+        int nSigOps = 0;
+        if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps))
         {
             if (pfMissingInputs)
                 *pfMissingInputs = true;
             return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
         }
+        // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
+        // attacks disallow transactions with more than one SigOp per 65 bytes.
+        // 65 bytes because that is the minimum size of an ECDSA signature
+        unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
+        if (nSigOps > nSize / 65 || nSize < 100)
+            return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
 
         // Don't accept it if it can't get into a block
         if (nFees < GetMinFee(1000, true, true))
@@ -826,8 +895,61 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
 }
 
 
-bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
-                                 CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee)
+bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
+                               bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
+{
+    if (IsCoinBase())
+        return true; // Coinbase transactions have no inputs to fetch.
+    
+    for (int i = 0; i < vin.size(); i++)
+    {
+        COutPoint prevout = vin[i].prevout;
+        if (inputsRet.count(prevout.hash))
+            continue; // Got it already
+
+        // Read txindex
+        CTxIndex& txindex = inputsRet[prevout.hash].first;
+        bool fFound = true;
+        if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
+        {
+            // Get txindex from current proposed changes
+            txindex = mapTestPool.find(prevout.hash)->second;
+        }
+        else
+        {
+            // Read txindex from txdb
+            fFound = txdb.ReadTxIndex(prevout.hash, txindex);
+        }
+        if (!fFound && (fBlock || fMiner))
+            return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
+
+        // Read txPrev
+        CTransaction& txPrev = inputsRet[prevout.hash].second;
+        if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
+        {
+            // Get prev tx from single transactions in memory
+            CRITICAL_BLOCK(cs_mapTransactions)
+            {
+                if (!mapTransactions.count(prevout.hash))
+                    return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
+                txPrev = mapTransactions[prevout.hash];
+            }
+            if (!fFound)
+                txindex.vSpent.resize(txPrev.vout.size());
+        }
+        else
+        {
+            // Get prev tx from disk
+            if (!txPrev.ReadFromDisk(txindex.pos))
+                return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
+        }
+    }
+    return true;
+}
+
+bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inputs,
+                                 map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
+                                 CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee)
 {
     // Take over previous transactions' spent pointers
     // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
@@ -839,43 +961,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
         for (int i = 0; i < vin.size(); i++)
         {
             COutPoint prevout = vin[i].prevout;
-
-            // Read txindex
-            CTxIndex txindex;
-            bool fFound = true;
-            if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
-            {
-                // Get txindex from current proposed changes
-                txindex = mapTestPool[prevout.hash];
-            }
-            else
-            {
-                // Read txindex from txdb
-                fFound = txdb.ReadTxIndex(prevout.hash, txindex);
-            }
-            if (!fFound && (fBlock || fMiner))
-                return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
-
-            // Read txPrev
-            CTransaction txPrev;
-            if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
-            {
-                // Get prev tx from single transactions in memory
-                CRITICAL_BLOCK(cs_mapTransactions)
-                {
-                    if (!mapTransactions.count(prevout.hash))
-                        return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
-                    txPrev = mapTransactions[prevout.hash];
-                }
-                if (!fFound)
-                    txindex.vSpent.resize(txPrev.vout.size());
-            }
-            else
-            {
-                // Get prev tx from disk
-                if (!txPrev.ReadFromDisk(txindex.pos))
-                    return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
-            }
+            assert(inputs.count(prevout.hash) > 0);
+            CTxIndex& txindex = inputs[prevout.hash].first;
+            CTransaction& txPrev = inputs[prevout.hash].second;
 
             if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
                 return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
@@ -891,7 +979,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
             // still computed and checked, and any change will be caught at the next checkpoint.
             if (!(fBlock && IsInitialBlockDownload()))
                 // Verify signature
-                if (!VerifySignature(txPrev, *this, i))
+                if (!VerifySignature(txPrev, *this, i, nSigOpsRet))
                     return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
 
             // Check for conflicts (double-spend)
@@ -965,7 +1053,8 @@ bool CTransaction::ClientConnectInputs()
                 return false;
 
             // Verify signature
-            if (!VerifySignature(txPrev, *this, i))
+            int nUnused = 0;
+            if (!VerifySignature(txPrev, *this, i, nUnused))
                 return error("ConnectInputs() : VerifySignature failed");
 
             ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@@ -1023,14 +1112,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
 
     map<uint256, CTxIndex> mapQueuedChanges;
     int64 nFees = 0;
+    int nSigOps = 0;
     BOOST_FOREACH(CTransaction& tx, vtx)
     {
         CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
         nTxPos += ::GetSerializeSize(tx, SER_DISK);
 
-        if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false))
+        map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
+        if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
+            return false;
+        if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps))
             return false;
+        if (nSigOps > MAX_BLOCK_SIGOPS)
+            return DoS(100, error("ConnectBlock() : too many sigops"));
     }
+
     // Write queued txindex changes
     for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
     {
@@ -1291,8 +1387,21 @@ bool CBlock::CheckBlock() const
         if (!tx.CheckTransaction())
             return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
 
-    // Check that it's not full of nonstandard transactions
-    if (GetSigOpCount() > MAX_BLOCK_SIGOPS)
+    // This code should be removed when a compatibility-breaking block chain split has passed.
+    // Compatibility check for old clients that counted sigops differently:
+    int nSigOps = 0;
+    BOOST_FOREACH(const CTransaction& tx, vtx)
+    {
+        BOOST_FOREACH(const CTxIn& txin, tx.vin)
+        {
+            nSigOps += txin.scriptSig.GetSigOpCount();
+        }
+        BOOST_FOREACH(const CTxOut& txout, tx.vout)
+        {
+            nSigOps += txout.scriptPubKey.GetSigOpCount();
+        }
+    }
+    if (nSigOps > MAX_BLOCK_SIGOPS)
         return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
 
     // Check merkleroot
@@ -2827,9 +2936,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
             unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
             if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
                 continue;
-            int nTxSigOps = tx.GetSigOpCount();
-            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
-                continue;
 
             // Transaction fee required depends on block size
             bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
@@ -2838,7 +2944,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
             // Connecting shouldn't fail due to dependency on other memory pool transactions
             // because we're already processing them in order of dependency
             map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
-            if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
+            map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
+            if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
+                continue;
+            int nTxSigOps = 0;
+            if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee))
+                continue;
+            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
                 continue;
             swap(mapTestPool, mapTestPoolTmp);
 
index 7f8da4e..bf31234 100644 (file)
@@ -493,26 +493,8 @@ public:
         return (vin.size() == 1 && vin[0].prevout.IsNull());
     }
 
-    int GetSigOpCount() const
-    {
-        int n = 0;
-        BOOST_FOREACH(const CTxIn& txin, vin)
-            n += txin.scriptSig.GetSigOpCount();
-        BOOST_FOREACH(const CTxOut& txout, vout)
-            n += txout.scriptPubKey.GetSigOpCount();
-        return n;
-    }
-
-    bool IsStandard() const
-    {
-        BOOST_FOREACH(const CTxIn& txin, vin)
-            if (!txin.scriptSig.IsPushOnly())
-                return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
-        BOOST_FOREACH(const CTxOut& txout, vout)
-            if (!::IsStandard(txout.scriptPubKey))
-                return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
-        return true;
-    }
+    bool IsStandard() const;
+    bool IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;
 
     int64 GetValueOut() const
     {
@@ -640,8 +622,11 @@ public:
     bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
     bool ReadFromDisk(COutPoint prevout);
     bool DisconnectInputs(CTxDB& txdb);
-    bool ConnectInputs(CTxDB& txdb, std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
-                       CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
+    bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
+                     bool fBlock, bool fMiner, std::map<uint256, std::pair<CTxIndex, CTransaction> >& inputsRet);
+    bool ConnectInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > inputs,
+                       std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
+                       CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0);
     bool ClientConnectInputs();
     bool CheckTransaction() const;
     bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
@@ -851,13 +836,6 @@ public:
         return (int64)nTime;
     }
 
-    int GetSigOpCount() const
-    {
-        int n = 0;
-        BOOST_FOREACH(const CTransaction& tx, vtx)
-            n += tx.GetSigOpCount();
-        return n;
-    }
 
 
     uint256 BuildMerkleTree() const
index 6a7913b..c103d57 100644 (file)
@@ -70,20 +70,186 @@ static inline void popstack(vector<valtype>& stack)
 }
 
 
-bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType)
+const char* GetTxnTypeName(txntype t)
+{
+    switch (t)
+    {
+    case TX_NONSTANDARD: return "nonstandard";
+    case TX_PUBKEY: return "pubkey";
+    case TX_PUBKEYHASH: return "pubkeyhash";
+    case TX_SCRIPTHASH: return "scripthash";
+    case TX_MULTISIG: return "multisig";
+    }
+    return NULL;
+}
+
+
+const char* GetOpName(opcodetype opcode)
+{
+    switch (opcode)
+    {
+    // push value
+    case OP_0                      : return "0";
+    case OP_PUSHDATA1              : return "OP_PUSHDATA1";
+    case OP_PUSHDATA2              : return "OP_PUSHDATA2";
+    case OP_PUSHDATA4              : return "OP_PUSHDATA4";
+    case OP_1NEGATE                : return "-1";
+    case OP_RESERVED               : return "OP_RESERVED";
+    case OP_1                      : return "1";
+    case OP_2                      : return "2";
+    case OP_3                      : return "3";
+    case OP_4                      : return "4";
+    case OP_5                      : return "5";
+    case OP_6                      : return "6";
+    case OP_7                      : return "7";
+    case OP_8                      : return "8";
+    case OP_9                      : return "9";
+    case OP_10                     : return "10";
+    case OP_11                     : return "11";
+    case OP_12                     : return "12";
+    case OP_13                     : return "13";
+    case OP_14                     : return "14";
+    case OP_15                     : return "15";
+    case OP_16                     : return "16";
+
+    // control
+    case OP_NOP                    : return "OP_NOP";
+    case OP_VER                    : return "OP_VER";
+    case OP_IF                     : return "OP_IF";
+    case OP_NOTIF                  : return "OP_NOTIF";
+    case OP_VERIF                  : return "OP_VERIF";
+    case OP_VERNOTIF               : return "OP_VERNOTIF";
+    case OP_ELSE                   : return "OP_ELSE";
+    case OP_ENDIF                  : return "OP_ENDIF";
+    case OP_VERIFY                 : return "OP_VERIFY";
+    case OP_RETURN                 : return "OP_RETURN";
+
+    // stack ops
+    case OP_TOALTSTACK             : return "OP_TOALTSTACK";
+    case OP_FROMALTSTACK           : return "OP_FROMALTSTACK";
+    case OP_2DROP                  : return "OP_2DROP";
+    case OP_2DUP                   : return "OP_2DUP";
+    case OP_3DUP                   : return "OP_3DUP";
+    case OP_2OVER                  : return "OP_2OVER";
+    case OP_2ROT                   : return "OP_2ROT";
+    case OP_2SWAP                  : return "OP_2SWAP";
+    case OP_IFDUP                  : return "OP_IFDUP";
+    case OP_DEPTH                  : return "OP_DEPTH";
+    case OP_DROP                   : return "OP_DROP";
+    case OP_DUP                    : return "OP_DUP";
+    case OP_NIP                    : return "OP_NIP";
+    case OP_OVER                   : return "OP_OVER";
+    case OP_PICK                   : return "OP_PICK";
+    case OP_ROLL                   : return "OP_ROLL";
+    case OP_ROT                    : return "OP_ROT";
+    case OP_SWAP                   : return "OP_SWAP";
+    case OP_TUCK                   : return "OP_TUCK";
+
+    // splice ops
+    case OP_CAT                    : return "OP_CAT";
+    case OP_SUBSTR                 : return "OP_SUBSTR";
+    case OP_LEFT                   : return "OP_LEFT";
+    case OP_RIGHT                  : return "OP_RIGHT";
+    case OP_SIZE                   : return "OP_SIZE";
+
+    // bit logic
+    case OP_INVERT                 : return "OP_INVERT";
+    case OP_AND                    : return "OP_AND";
+    case OP_OR                     : return "OP_OR";
+    case OP_XOR                    : return "OP_XOR";
+    case OP_EQUAL                  : return "OP_EQUAL";
+    case OP_EQUALVERIFY            : return "OP_EQUALVERIFY";
+    case OP_RESERVED1              : return "OP_RESERVED1";
+    case OP_RESERVED2              : return "OP_RESERVED2";
+
+    // numeric
+    case OP_1ADD                   : return "OP_1ADD";
+    case OP_1SUB                   : return "OP_1SUB";
+    case OP_2MUL                   : return "OP_2MUL";
+    case OP_2DIV                   : return "OP_2DIV";
+    case OP_NEGATE                 : return "OP_NEGATE";
+    case OP_ABS                    : return "OP_ABS";
+    case OP_NOT                    : return "OP_NOT";
+    case OP_0NOTEQUAL              : return "OP_0NOTEQUAL";
+    case OP_ADD                    : return "OP_ADD";
+    case OP_SUB                    : return "OP_SUB";
+    case OP_MUL                    : return "OP_MUL";
+    case OP_DIV                    : return "OP_DIV";
+    case OP_MOD                    : return "OP_MOD";
+    case OP_LSHIFT                 : return "OP_LSHIFT";
+    case OP_RSHIFT                 : return "OP_RSHIFT";
+    case OP_BOOLAND                : return "OP_BOOLAND";
+    case OP_BOOLOR                 : return "OP_BOOLOR";
+    case OP_NUMEQUAL               : return "OP_NUMEQUAL";
+    case OP_NUMEQUALVERIFY         : return "OP_NUMEQUALVERIFY";
+    case OP_NUMNOTEQUAL            : return "OP_NUMNOTEQUAL";
+    case OP_LESSTHAN               : return "OP_LESSTHAN";
+    case OP_GREATERTHAN            : return "OP_GREATERTHAN";
+    case OP_LESSTHANOREQUAL        : return "OP_LESSTHANOREQUAL";
+    case OP_GREATERTHANOREQUAL     : return "OP_GREATERTHANOREQUAL";
+    case OP_MIN                    : return "OP_MIN";
+    case OP_MAX                    : return "OP_MAX";
+    case OP_WITHIN                 : return "OP_WITHIN";
+
+    // crypto
+    case OP_RIPEMD160              : return "OP_RIPEMD160";
+    case OP_SHA1                   : return "OP_SHA1";
+    case OP_SHA256                 : return "OP_SHA256";
+    case OP_HASH160                : return "OP_HASH160";
+    case OP_HASH256                : return "OP_HASH256";
+    case OP_CODESEPARATOR          : return "OP_CODESEPARATOR";
+    case OP_CHECKSIG               : return "OP_CHECKSIG";
+    case OP_CHECKSIGVERIFY         : return "OP_CHECKSIGVERIFY";
+    case OP_CHECKMULTISIG          : return "OP_CHECKMULTISIG";
+    case OP_CHECKMULTISIGVERIFY    : return "OP_CHECKMULTISIGVERIFY";
+
+    // meta
+    case OP_EVAL                   : return "OP_EVAL";
+
+    // expanson
+    case OP_NOP2                   : return "OP_NOP2";
+    case OP_NOP3                   : return "OP_NOP3";
+    case OP_NOP4                   : return "OP_NOP4";
+    case OP_NOP5                   : return "OP_NOP5";
+    case OP_NOP6                   : return "OP_NOP6";
+    case OP_NOP7                   : return "OP_NOP7";
+    case OP_NOP8                   : return "OP_NOP8";
+    case OP_NOP9                   : return "OP_NOP9";
+    case OP_NOP10                  : return "OP_NOP10";
+
+
+
+    // template matching params
+    case OP_SCRIPTHASH             : return "OP_SCRIPTHASH";
+    case OP_PUBKEYHASH             : return "OP_PUBKEYHASH";
+    case OP_PUBKEY                 : return "OP_PUBKEY";
+
+    case OP_INVALIDOPCODE          : return "OP_INVALIDOPCODE";
+    default:
+        return "OP_UNKNOWN";
+    }
+}
+
+
+//
+// Returns true if script is valid.
+//
+bool EvalScriptInner(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType,
+                     CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount, int nRecurseDepth)
 {
     CAutoBN_CTX pctx;
     CScript::const_iterator pc = script.begin();
     CScript::const_iterator pend = script.end();
-    CScript::const_iterator pbegincodehash = script.begin();
     opcodetype opcode;
     valtype vchPushValue;
     vector<bool> vfExec;
     vector<valtype> altstack;
     if (script.size() > 10000)
         return false;
-    int nOpCount = 0;
 
+    // Limit OP_EVAL recursion
+    if (nRecurseDepth > 2)
+        return false;
 
     try
     {
@@ -155,7 +321,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
                 // Control
                 //
                 case OP_NOP:
-                case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
+                case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
                 case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
                 break;
 
@@ -751,12 +917,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
                     //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");
 
                     // Subset of script starting at the most recent codeseparator
-                    CScript scriptCode(pbegincodehash, pend);
+                    CScript scriptCode(pbegincodehash, pendcodehash);
 
                     // Drop the signature, since there's no way for a signature to sign itself
                     scriptCode.FindAndDelete(CScript(vchSig));
 
                     bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+                    nSigOpCount++;
 
                     popstack(stack);
                     popstack(stack);
@@ -800,7 +967,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
                         return false;
 
                     // Subset of script starting at the most recent codeseparator
-                    CScript scriptCode(pbegincodehash, pend);
+                    CScript scriptCode(pbegincodehash, pendcodehash);
 
                     // Drop the signatures, since there's no way for a signature to sign itself
                     for (int k = 0; k < nSigsCount; k++)
@@ -823,6 +990,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
                         }
                         ikey++;
                         nKeysCount--;
+                        nSigOpCount++;
 
                         // If there are more signatures left than keys left,
                         // then too many signatures have failed
@@ -844,6 +1012,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
                 }
                 break;
 
+                case OP_EVAL:
+                {
+                    // Evaluate the top item on the stack as a Script
+                    // [serialized script ] -- [result(s) of executing script]
+                    if (stack.size() < 1)
+                        return false;
+                    valtype& vchScript = stacktop(-1);
+                    CScript subscript(vchScript.begin(), vchScript.end());
+                    popstack(stack);
+                    
+                    // Codeseparators not allowed
+                    if (subscript.Find(OP_CODESEPARATOR))
+                        return false;
+
+                    if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType,
+                                         pbegincodehash, pendcodehash, nOpCount, nSigOpCount, nRecurseDepth++))
+                        return false;
+                }
+                break;
+
                 default:
                     return false;
             }
@@ -865,6 +1053,17 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
     return true;
 }
 
+bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script,
+                const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet)
+{
+    CScript::const_iterator pbegincodehash = script.begin();
+    CScript::const_iterator pendcodehash = script.end();
+
+    int nOpCount = 0;
+    return EvalScriptInner(stack, script, txTo, nIn, nHashType, pbegincodehash, pendcodehash,
+                           nOpCount, nSigOpCountRet, 0);
+}
+
 
 
 
@@ -964,38 +1163,35 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
 
 
 //
-// Returns lists of public keys (or public key hashes), any one of which can
-// satisfy scriptPubKey
+// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
 //
-bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet)
+bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
 {
     // Templates
-    static vector<CScript> vTemplates;
-    if (vTemplates.empty())
+    static map<txntype, CScript> mTemplates;
+    if (mTemplates.empty())
     {
         // Standard tx, sender provides pubkey, receiver adds signature
-        vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG);
+        mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
 
         // 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);
+        mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
 
-        // 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 N pubkeys, receivers provides M signatures
+        mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << 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);
+        // Sender provides script hash, receiver provides script and
+        // as many signatures as required to satisfy script
+        mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_DUP << OP_HASH160 << OP_SCRIPTHASH << OP_EQUALVERIFY << OP_EVAL));
     }
 
     // Scan templates
     const CScript& script1 = scriptPubKey;
-    BOOST_FOREACH(const CScript& script2, vTemplates)
+    BOOST_FOREACH(const PAIRTYPE(txntype, CScript)& tplate, mTemplates)
     {
+        const CScript& script2 = tplate.second;
         vSolutionsRet.clear();
 
-        vector<pair<opcodetype, valtype> > currentSolution;
         opcodetype opcode1, opcode2;
         vector<unsigned char> vch1, vch2;
 
@@ -1006,218 +1202,333 @@ bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype>
         {
             if (pc1 == script1.end() && pc2 == script2.end())
             {
-                return !vSolutionsRet.empty();
+                // Found a match
+                typeRet = tplate.first;
+                if (typeRet == TX_MULTISIG)
+                {
+                    // Additional checks for TX_MULTISIG:
+                    unsigned char m = vSolutionsRet.front()[0];
+                    unsigned char n = vSolutionsRet.back()[0];
+                    if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
+                        return false;
+                }
+                return true;
             }
             if (!script1.GetOp(pc1, opcode1, vch1))
                 break;
             if (!script2.GetOp(pc2, opcode2, vch2))
                 break;
+
+            // Template matching opcodes:
+            if (opcode2 == OP_PUBKEYS)
+            {
+                while (vch1.size() >= 33 && vch1.size() <= 120)
+                {
+                    vSolutionsRet.push_back(vch1);
+                    if (!script1.GetOp(pc1, opcode1, vch1))
+                        break;
+                }
+                if (!script2.GetOp(pc2, opcode2, vch2))
+                    break;
+                // Normal situation is to fall through
+                // to other if/else statments
+            }
+
             if (opcode2 == OP_PUBKEY)
             {
                 if (vch1.size() < 33 || vch1.size() > 120)
                     break;
-                currentSolution.push_back(make_pair(opcode2, vch1));
+                vSolutionsRet.push_back(vch1);
             }
             else if (opcode2 == OP_PUBKEYHASH)
             {
                 if (vch1.size() != sizeof(uint160))
                     break;
-                currentSolution.push_back(make_pair(opcode2, vch1));
+                vSolutionsRet.push_back(vch1);
             }
-            else if (opcode2 == OP_CHECKSIG)
+            else if (opcode2 == OP_SCRIPTHASH)
             {
-                vSolutionsRet.push_back(currentSolution);
-                currentSolution.clear();
+                if (vch1.size() != sizeof(uint160))
+                    break;
+                vSolutionsRet.push_back(vch1);
             }
-            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)
+            else if (opcode2 == OP_SMALLINTEGER)
+            {   // Single-byte small integer pushed onto vSolutions
+                if (opcode1 == OP_0 ||
+                    (opcode1 >= OP_1 && opcode1 <= OP_16))
                 {
-                    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();
+                    char n = (char)CScript::DecodeOP_N(opcode1);
+                    vSolutionsRet.push_back(valtype(1, n));
                 }
+                else
+                    break;
             }
             else if (opcode1 != opcode2 || vch1 != vch2)
             {
+                // Others must match exactly
                 break;
             }
         }
     }
 
     vSolutionsRet.clear();
+    typeRet = TX_NONSTANDARD;
     return false;
 }
 
 
-bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
+bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
 {
-    scriptSigRet.clear();
+    CKey key;
+    if (!keystore.GetKey(address, key))
+        return false;
 
-    vector<vector<pair<opcodetype, valtype> > > vSolutions;
-    if (!Solver(scriptPubKey, vSolutions))
+    vector<unsigned char> vchSig;
+    if (!key.Sign(hash, vchSig))
         return false;
+    vchSig.push_back((unsigned char)nHashType);
+    scriptSigRet << vchSig;
 
-    // See if we have all the keys for any of the solutions:
-    int whichSolution = -1;
-    for (int i = 0; i < vSolutions.size(); i++)
-    {
-        int keysFound = 0;
-        CScript scriptSig;
+    return true;
+}
 
-        BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
-        {
-            if (item.first == OP_PUBKEY)
-            {
-                const valtype& vchPubKey = item.second;
-                CKey key;
-                vector<unsigned char> vchSig;
-                if (keystore.GetKey(Hash160(vchPubKey), key) && key.GetPubKey() == vchPubKey
-                    && hash != 0 && key.Sign(hash, vchSig))
-                {
-                    vchSig.push_back((unsigned char)nHashType);
-                    scriptSig << vchSig;
-                    ++keysFound;
-                }
-            }
-            else if (item.first == OP_PUBKEYHASH)
-            {
-                CKey key;
-                vector<unsigned char> vchSig;
-                if (keystore.GetKey(uint160(item.second), key) 
-                    && hash != 0 && key.Sign(hash, vchSig))
-                {
-                    vchSig.push_back((unsigned char)nHashType);
-                    scriptSig << vchSig << key.GetPubKey();
-                    ++keysFound;
-                }
-            }
-        }
-        if (keysFound == vSolutions[i].size())
+bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
+{
+    int nSigned = 0;
+    int nRequired = multisigdata.front()[0];
+    for (vector<valtype>::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++)
+    {
+        const valtype& pubkey = *it;
+        CBitcoinAddress address;
+        address.SetPubKey(pubkey);
+        if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
         {
-            whichSolution = i;
-            scriptSigRet = scriptSig;
-            break;
+            ++nSigned;
+            if (nSigned == nRequired) break;
         }
     }
-    if (whichSolution == -1)
+    return nSigned==nRequired;
+}
+
+//
+// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type.
+// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed).
+// Returns true if scriptPubKey could be completely satisified.
+//
+bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
+{
+    scriptSigRet.clear();
+
+    txntype whichType;
+    vector<valtype> vSolutions;
+    if (!Solver(scriptPubKey, whichType, vSolutions))
         return false;
 
-    // CHECKMULTISIG bug workaround:
-    if (vSolutions.size() != 1 ||
-        vSolutions[0].size() != 1)
+    CBitcoinAddress address;
+    valtype subscript;
+    switch (whichType)
     {
-        scriptSigRet.insert(scriptSigRet.begin(), OP_0);
+    case TX_NONSTANDARD:
+        return false;
+    case TX_PUBKEY:
+        address.SetPubKey(vSolutions[0]);
+        return Sign1(address, keystore, hash, nHashType, scriptSigRet);
+    case TX_PUBKEYHASH:
+        address.SetHash160(uint160(vSolutions[0]));
+        if (!Sign1(address, keystore, hash, nHashType, scriptSigRet))
+            return false;
+        else
+        {
+            valtype vch;
+            keystore.GetPubKey(address, vch);
+            scriptSigRet << vch;
+        }
+        break;
+    case TX_SCRIPTHASH:
+        if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
+            return false;
+        if (!Solver(keystore, CScript(subscript.begin(), subscript.end()), hash, nHashType, scriptSigRet))
+            return false;
+        if (hash != 0)
+            scriptSigRet << subscript; // signatures AND serialized script
+        break;
+    case TX_MULTISIG:
+        scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
+        return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet));
     }
-
     return true;
 }
 
 
 bool IsStandard(const CScript& scriptPubKey)
 {
-    vector<vector<pair<opcodetype, valtype> > > vSolutions;
-    return Solver(scriptPubKey, vSolutions);
+    vector<valtype> vSolutions;
+    txntype whichType;
+    if (!Solver(scriptPubKey, whichType, vSolutions))
+        return false;
+
+    if (whichType == TX_MULTISIG)
+    {
+        unsigned char m = vSolutions.front()[0];
+        unsigned char n = vSolutions.back()[0];
+        // Support up to x-of-3 multisig txns as standard
+        if (n < 1 || n > 3)
+            return false;
+        if (m < 1 || m > n)
+            return false;
+    }
+
+    return whichType != TX_NONSTANDARD;
 }
 
 
+int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
+{
+    int nResult = 0;
+    BOOST_FOREACH(const valtype& pubkey, pubkeys)
+    {
+        CBitcoinAddress address;
+        address.SetPubKey(pubkey);
+        if (keystore.HaveKey(address))
+            ++nResult;
+    }
+    return nResult;
+}
+
 bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
 {
-    vector<vector<pair<opcodetype, valtype> > > vSolutions;
-    if (!Solver(scriptPubKey, vSolutions))
+    vector<valtype> vSolutions;
+    txntype whichType;
+    if (!Solver(scriptPubKey, whichType, vSolutions))
         return false;
 
-    int keysFound = 0;
-    int keysRequired = 0;
-    for (int i = 0; i < vSolutions.size(); i++)
+    CBitcoinAddress address;
+    switch (whichType)
     {
-        BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
-        {
-            ++keysRequired;
-            if (item.first == OP_PUBKEY)
-            {
-                const valtype& vchPubKey = item.second;
-                vector<unsigned char> vchPubKeyFound;
-                if (keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound) && vchPubKeyFound == vchPubKey)
-                    ++keysFound;
-            }
-            else if (item.first == OP_PUBKEYHASH)
-            {
-                if (keystore.HaveKey(uint160(item.second)))
-                    ++keysFound;
-            }
-        }
+    case TX_NONSTANDARD:
+        return false;
+    case TX_PUBKEY:
+        address.SetPubKey(vSolutions[0]);
+        return keystore.HaveKey(address);
+    case TX_PUBKEYHASH:
+        address.SetHash160(uint160(vSolutions[0]));
+        return keystore.HaveKey(address);
+    case TX_SCRIPTHASH:
+    {
+        valtype subscript;
+        if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
+            return false;
+        return IsMine(keystore, CScript(subscript.begin(), subscript.end()));
     }
-
-    // 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);
+    case TX_MULTISIG:
+    {
+        // 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
+        // in shared-wallet situations.
+        vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
+        return HaveKeys(vSolutions, keystore);
+    }
+    }
+    return false;
 }
 
 bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
 {
-    vector<vector<pair<opcodetype, valtype> > > vSolutions;
-    if (!Solver(scriptPubKey, vSolutions))
+    vector<valtype> vSolutions;
+    txntype whichType;
+    if (!Solver(scriptPubKey, whichType, vSolutions))
         return false;
 
-    for (int i = 0; i < vSolutions.size(); i++)
+    if (whichType == TX_PUBKEY)
     {
-        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;
+        addressRet.SetPubKey(vSolutions[0]);
+        return true;
     }
+    else if (whichType == TX_PUBKEYHASH)
+    {
+        addressRet.SetHash160(uint160(vSolutions[0]));
+        return true;
+    }
+    else if (whichType == TX_SCRIPTHASH)
+    {
+        addressRet.SetScriptHash160(uint160(vSolutions[0]));
+        return true;
+    }
+    // Multisig txns have more than one address...
     return false;
 }
 
+bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txntype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
+{
+    addressRet.clear();
+    typeRet = TX_NONSTANDARD;
+    vector<valtype> vSolutions;
+    if (!Solver(scriptPubKey, typeRet, vSolutions))
+        return false;
 
+    if (typeRet == TX_MULTISIG)
+    {
+        nRequiredRet = vSolutions.front()[0];
+        int n = vSolutions.back()[0];
+        for (vector<valtype>::const_iterator it = vSolutions.begin()+1; it != vSolutions.begin()+vSolutions.size()-1; it++)
+        {
+            CBitcoinAddress address;
+            address.SetPubKey(*it);
+            addressRet.push_back(address);
+        }
+    }
+    else
+    {
+        nRequiredRet = 1;
+        CBitcoinAddress address;
+        if (typeRet == TX_PUBKEYHASH)
+            address.SetHash160(uint160(vSolutions.front()));
+        else if (typeRet == TX_SCRIPTHASH)
+            address.SetScriptHash160(uint160(vSolutions.front()));
+        else if (typeRet == TX_PUBKEY)
+            address.SetPubKey(vSolutions.front());
+        addressRet.push_back(address);
+    }
 
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
+    return true;
+}
+
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
 {
     vector<vector<unsigned char> > stack;
-    if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
+    if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, nSigOpCountRet))
         return false;
-    if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
+    if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, nSigOpCountRet))
         return false;
     if (stack.empty())
         return false;
-    return CastToBool(stack.back());
+    bool fResult = CastToBool(stack.back());
+
+    // This code should be removed when a compatibility-breaking block chain split has passed.
+    // Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains
+    // OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op:
+    if (scriptSig.Find(OP_EVAL)+scriptPubKey.Find(OP_EVAL) > 0)
+    {
+        int nUnused = 0;
+        stack.clear();
+        CScript sigCopy = scriptSig;
+        sigCopy.FindAndDelete(CScript(OP_EVAL));
+        CScript pubKeyCopy = scriptPubKey;
+        pubKeyCopy.FindAndDelete(CScript(OP_EVAL));
+
+        if (!EvalScript(stack, sigCopy, txTo, nIn, nHashType, nUnused))
+            return false;
+        if (!EvalScript(stack, pubKeyCopy, txTo, nIn, nHashType, nUnused))
+            return false;
+        if (stack.empty())
+            return false;
+        if (fResult != CastToBool(stack.back()))
+            return false;
+    }
+
+    return fResult;
 }
 
 
@@ -1238,15 +1549,16 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
     txin.scriptSig = scriptPrereq + txin.scriptSig;
 
     // Test solution
+    int nUnused = 0;
     if (scriptPrereq.empty())
-        if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0))
+        if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0))
             return false;
 
     return true;
 }
 
 
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType)
+bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
 {
     assert(nIn < txTo.vin.size());
     const CTxIn& txin = txTo.vin[nIn];
@@ -1257,27 +1569,35 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
     if (txin.prevout.hash != txFrom.GetHash())
         return false;
 
-    if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType))
+    if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType))
         return false;
 
     return true;
 }
 
-void CScript::SetMultisigAnd(const std::vector<CKey>& keys)
+void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
 {
-    assert(keys.size() >= 2);
     this->clear();
-    *this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
+    if (address.IsScript())
+        *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_EVAL;
+    else
+        *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
 }
-void CScript::SetMultisigOr(const std::vector<CKey>& keys)
+
+void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
 {
-    assert(keys.size() >= 2);
     this->clear();
-    *this << OP_1 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
+
+    *this << EncodeOP_N(nRequired);
+    BOOST_FOREACH(const CKey& key, keys)
+        *this << key.GetPubKey();
+    *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
 }
-void CScript::SetMultisigEscrow(const std::vector<CKey>& keys)
+
+void CScript::SetEval(const CScript& subscript)
 {
-    assert(keys.size() >= 3);
+    assert(!subscript.empty());
+    uint160 subscriptHash = Hash160(subscript);
     this->clear();
-    *this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << keys[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
+    *this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL;
 }
index a5a1e18..ee0be02 100644 (file)
@@ -24,6 +24,17 @@ enum
 };
 
 
+enum txntype
+{
+    TX_NONSTANDARD,
+    // 'standard' transaction types:
+    TX_PUBKEY,
+    TX_PUBKEYHASH,
+    TX_SCRIPTHASH,
+    TX_MULTISIG,
+};
+
+const char* GetTxnTypeName(txntype t);
 
 enum opcodetype
 {
@@ -147,8 +158,10 @@ enum opcodetype
     OP_CHECKMULTISIG,
     OP_CHECKMULTISIGVERIFY,
 
+    // meta
+    OP_EVAL, // Was OP_NOP1
+
     // expansion
-    OP_NOP1,
     OP_NOP2,
     OP_NOP3,
     OP_NOP4,
@@ -162,162 +175,16 @@ enum opcodetype
 
 
     // template matching params
+    OP_SMALLINTEGER = 0xfa,
+    OP_PUBKEYS = 0xfb,
+    OP_SCRIPTHASH = 0xfc,
     OP_PUBKEYHASH = 0xfd,
     OP_PUBKEY = 0xfe,
 
     OP_INVALIDOPCODE = 0xff,
 };
 
-
-
-
-
-
-
-
-inline const char* GetOpName(opcodetype opcode)
-{
-    switch (opcode)
-    {
-    // push value
-    case OP_0                      : return "0";
-    case OP_PUSHDATA1              : return "OP_PUSHDATA1";
-    case OP_PUSHDATA2              : return "OP_PUSHDATA2";
-    case OP_PUSHDATA4              : return "OP_PUSHDATA4";
-    case OP_1NEGATE                : return "-1";
-    case OP_RESERVED               : return "OP_RESERVED";
-    case OP_1                      : return "1";
-    case OP_2                      : return "2";
-    case OP_3                      : return "3";
-    case OP_4                      : return "4";
-    case OP_5                      : return "5";
-    case OP_6                      : return "6";
-    case OP_7                      : return "7";
-    case OP_8                      : return "8";
-    case OP_9                      : return "9";
-    case OP_10                     : return "10";
-    case OP_11                     : return "11";
-    case OP_12                     : return "12";
-    case OP_13                     : return "13";
-    case OP_14                     : return "14";
-    case OP_15                     : return "15";
-    case OP_16                     : return "16";
-
-    // control
-    case OP_NOP                    : return "OP_NOP";
-    case OP_VER                    : return "OP_VER";
-    case OP_IF                     : return "OP_IF";
-    case OP_NOTIF                  : return "OP_NOTIF";
-    case OP_VERIF                  : return "OP_VERIF";
-    case OP_VERNOTIF               : return "OP_VERNOTIF";
-    case OP_ELSE                   : return "OP_ELSE";
-    case OP_ENDIF                  : return "OP_ENDIF";
-    case OP_VERIFY                 : return "OP_VERIFY";
-    case OP_RETURN                 : return "OP_RETURN";
-
-    // stack ops
-    case OP_TOALTSTACK             : return "OP_TOALTSTACK";
-    case OP_FROMALTSTACK           : return "OP_FROMALTSTACK";
-    case OP_2DROP                  : return "OP_2DROP";
-    case OP_2DUP                   : return "OP_2DUP";
-    case OP_3DUP                   : return "OP_3DUP";
-    case OP_2OVER                  : return "OP_2OVER";
-    case OP_2ROT                   : return "OP_2ROT";
-    case OP_2SWAP                  : return "OP_2SWAP";
-    case OP_IFDUP                  : return "OP_IFDUP";
-    case OP_DEPTH                  : return "OP_DEPTH";
-    case OP_DROP                   : return "OP_DROP";
-    case OP_DUP                    : return "OP_DUP";
-    case OP_NIP                    : return "OP_NIP";
-    case OP_OVER                   : return "OP_OVER";
-    case OP_PICK                   : return "OP_PICK";
-    case OP_ROLL                   : return "OP_ROLL";
-    case OP_ROT                    : return "OP_ROT";
-    case OP_SWAP                   : return "OP_SWAP";
-    case OP_TUCK                   : return "OP_TUCK";
-
-    // splice ops
-    case OP_CAT                    : return "OP_CAT";
-    case OP_SUBSTR                 : return "OP_SUBSTR";
-    case OP_LEFT                   : return "OP_LEFT";
-    case OP_RIGHT                  : return "OP_RIGHT";
-    case OP_SIZE                   : return "OP_SIZE";
-
-    // bit logic
-    case OP_INVERT                 : return "OP_INVERT";
-    case OP_AND                    : return "OP_AND";
-    case OP_OR                     : return "OP_OR";
-    case OP_XOR                    : return "OP_XOR";
-    case OP_EQUAL                  : return "OP_EQUAL";
-    case OP_EQUALVERIFY            : return "OP_EQUALVERIFY";
-    case OP_RESERVED1              : return "OP_RESERVED1";
-    case OP_RESERVED2              : return "OP_RESERVED2";
-
-    // numeric
-    case OP_1ADD                   : return "OP_1ADD";
-    case OP_1SUB                   : return "OP_1SUB";
-    case OP_2MUL                   : return "OP_2MUL";
-    case OP_2DIV                   : return "OP_2DIV";
-    case OP_NEGATE                 : return "OP_NEGATE";
-    case OP_ABS                    : return "OP_ABS";
-    case OP_NOT                    : return "OP_NOT";
-    case OP_0NOTEQUAL              : return "OP_0NOTEQUAL";
-    case OP_ADD                    : return "OP_ADD";
-    case OP_SUB                    : return "OP_SUB";
-    case OP_MUL                    : return "OP_MUL";
-    case OP_DIV                    : return "OP_DIV";
-    case OP_MOD                    : return "OP_MOD";
-    case OP_LSHIFT                 : return "OP_LSHIFT";
-    case OP_RSHIFT                 : return "OP_RSHIFT";
-    case OP_BOOLAND                : return "OP_BOOLAND";
-    case OP_BOOLOR                 : return "OP_BOOLOR";
-    case OP_NUMEQUAL               : return "OP_NUMEQUAL";
-    case OP_NUMEQUALVERIFY         : return "OP_NUMEQUALVERIFY";
-    case OP_NUMNOTEQUAL            : return "OP_NUMNOTEQUAL";
-    case OP_LESSTHAN               : return "OP_LESSTHAN";
-    case OP_GREATERTHAN            : return "OP_GREATERTHAN";
-    case OP_LESSTHANOREQUAL        : return "OP_LESSTHANOREQUAL";
-    case OP_GREATERTHANOREQUAL     : return "OP_GREATERTHANOREQUAL";
-    case OP_MIN                    : return "OP_MIN";
-    case OP_MAX                    : return "OP_MAX";
-    case OP_WITHIN                 : return "OP_WITHIN";
-
-    // crypto
-    case OP_RIPEMD160              : return "OP_RIPEMD160";
-    case OP_SHA1                   : return "OP_SHA1";
-    case OP_SHA256                 : return "OP_SHA256";
-    case OP_HASH160                : return "OP_HASH160";
-    case OP_HASH256                : return "OP_HASH256";
-    case OP_CODESEPARATOR          : return "OP_CODESEPARATOR";
-    case OP_CHECKSIG               : return "OP_CHECKSIG";
-    case OP_CHECKSIGVERIFY         : return "OP_CHECKSIGVERIFY";
-    case OP_CHECKMULTISIG          : return "OP_CHECKMULTISIG";
-    case OP_CHECKMULTISIGVERIFY    : return "OP_CHECKMULTISIGVERIFY";
-
-    // expanson
-    case OP_NOP1                   : return "OP_NOP1";
-    case OP_NOP2                   : return "OP_NOP2";
-    case OP_NOP3                   : return "OP_NOP3";
-    case OP_NOP4                   : return "OP_NOP4";
-    case OP_NOP5                   : return "OP_NOP5";
-    case OP_NOP6                   : return "OP_NOP6";
-    case OP_NOP7                   : return "OP_NOP7";
-    case OP_NOP8                   : return "OP_NOP8";
-    case OP_NOP9                   : return "OP_NOP9";
-    case OP_NOP10                  : return "OP_NOP10";
-
-
-
-    // template matching params
-    case OP_PUBKEYHASH             : return "OP_PUBKEYHASH";
-    case OP_PUBKEY                 : return "OP_PUBKEY";
-
-    case OP_INVALIDOPCODE          : return "OP_INVALIDOPCODE";
-    default:
-        return "OP_UNKNOWN";
-    }
-};
-
+const char* GetOpName(opcodetype opcode);
 
 
 
@@ -574,6 +441,7 @@ public:
         return true;
     }
 
+    // Encode/decode small integers:
     static int DecodeOP_N(opcodetype opcode)
     {
         if (opcode == OP_0)
@@ -581,22 +449,44 @@ public:
         assert(opcode >= OP_1 && opcode <= OP_16);
         return (int)opcode - (int)(OP_1 - 1);
     }
+    static opcodetype EncodeOP_N(int n)
+    {
+        assert(n >= 0 && n <= 16);
+        if (n == 0)
+            return OP_0;
+        return (opcodetype)(OP_1+n-1);
+    }
 
-    void FindAndDelete(const CScript& b)
+    int FindAndDelete(const CScript& b)
     {
+        int nFound = 0;
         if (b.empty())
-            return;
+            return nFound;
         iterator pc = begin();
         opcodetype opcode;
         do
         {
             while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
+            {
                 erase(pc, pc + b.size());
+                ++nFound;
+            }
         }
         while (GetOp(pc, opcode));
+        return nFound;
+    }
+    int Find(opcodetype op) const
+    {
+        int nFound = 0;
+        opcodetype opcode;
+        for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);)
+            if (opcode == op)
+                ++nFound;
+        return nFound;
     }
 
-
+    // This method should be removed when a compatibility-breaking block chain split has passed.
+    // Compatibility method for old clients that count sigops differently:
     int GetSigOpCount() const
     {
         int n = 0;
@@ -614,11 +504,9 @@ public:
         return n;
     }
 
-
+    // Called by CTransaction::IsStandard
     bool IsPushOnly() const
     {
-        if (size() > 200)
-            return false;
         const_iterator pc = begin();
         while (pc < end())
         {
@@ -632,19 +520,13 @@ public:
     }
 
 
-    void SetBitcoinAddress(const CBitcoinAddress& address)
-    {
-        this->clear();
-        *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
-    }
-
+    void SetBitcoinAddress(const CBitcoinAddress& address);
     void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
     {
         SetBitcoinAddress(CBitcoinAddress(vchPubKey));
     }
-    void SetMultisigAnd(const std::vector<CKey>& keys);
-    void SetMultisigOr(const std::vector<CKey>& keys);
-    void SetMultisigEscrow(const std::vector<CKey>& keys);
+    void SetMultisig(int nRequired, const std::vector<CKey>& keys);
+    void SetEval(const CScript& subscript);
 
 
     void PrintHex() const
@@ -685,14 +567,14 @@ public:
 
 
 
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet);
 
-
-bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
-
+bool Solver(const CScript& scriptPubKey, txntype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
 bool IsStandard(const CScript& scriptPubKey);
 bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
 bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet);
+bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txntype& typeRet, std::vector<CBitcoinAddress>& addressRet, int& nRequiredRet);
 bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript());
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0);
+bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0);
 
 #endif
index 459d112..75c764d 100644 (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);
     }
 }
 
diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp
new file mode 100644 (file)
index 0000000..857d04b
--- /dev/null
@@ -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()
index f3fa5c3..3d1c218 100644 (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));
 }    
 
 
index a662111..5d44b1f 100644 (file)
@@ -42,6 +42,15 @@ bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector
     return false;
 }
 
+bool CWallet::AddCScript(const uint160 &hash, const std::vector<unsigned char>& data)
+{
+    if (!CCryptoKeyStore::AddCScript(hash, data))
+        return false;
+    if (!fFileBacked)
+        return true;
+    return CWalletDB(strWalletFile).WriteCScript(hash, data);
+}
+
 bool CWallet::Unlock(const SecureString& strWalletPassphrase)
 {
     if (!IsLocked())
@@ -374,6 +383,16 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
     return 0;
 }
 
+bool CWallet::IsChange(const CTxOut& txout) const
+{
+    CBitcoinAddress address;
+    if (ExtractAddress(txout.scriptPubKey, this, address) && !address.IsScript())
+        CRITICAL_BLOCK(cs_wallet)
+            if (!mapAddressBook.count(address))
+                return true;
+    return false;
+}
+
 int64 CWalletTx::GetTxTime() const
 {
     return nTimeReceived;
@@ -443,8 +462,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
         nFee = nDebit - nValueOut;
     }
 
-    // Sent/received.  Standard client will never generate a send-to-multiple-recipients,
-    // but non-standard clients might (so return a list of address/amount pairs)
+    // Sent/received.
     BOOST_FOREACH(const CTxOut& txout, vout)
     {
         CBitcoinAddress address;
index 78f055a..34090ec 100644 (file)
@@ -69,6 +69,8 @@ public:
     bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
     // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
     bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
+    bool AddCScript(const uint160& hash, const std::vector<unsigned char>& data);
+    bool LoadCScript(const uint160& hash, const std::vector<unsigned char>& data) { return CCryptoKeyStore::AddCScript(hash, data); }
 
     bool Unlock(const SecureString& strWalletPassphrase);
     bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
@@ -114,15 +116,7 @@ public:
             throw std::runtime_error("CWallet::GetCredit() : value out of range");
         return (IsMine(txout) ? txout.nValue : 0);
     }
-    bool IsChange(const CTxOut& txout) const
-    {
-        CBitcoinAddress address;
-        if (ExtractAddress(txout.scriptPubKey, this, address))
-            CRITICAL_BLOCK(cs_wallet)
-                if (!mapAddressBook.count(address))
-                    return true;
-        return false;
-    }
+    bool IsChange(const CTxOut& txout) const;
     int64 GetChange(const CTxOut& txout) const
     {
         if (!MoneyRange(txout.nValue))