Network stack refactor
[novacoin.git] / src / main.cpp
index 1b5bf52..9a3ce65 100644 (file)
@@ -35,7 +35,6 @@ map<COutPoint, CInPoint> mapNextTx;
 map<uint256, CBlockIndex*> mapBlockIndex;
 uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
 static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
-const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
 CBlockIndex* pindexGenesisBlock = NULL;
 int nBestHeight = -1;
 CBigNum bnBestChainWork = 0;
@@ -250,13 +249,13 @@ 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
+        // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
+        // in an OP_EVAL, which is 3 ~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.size() > 500)
+            return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size());
         if (!txin.scriptSig.IsPushOnly())
-            return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
+            return error("nonstandard txin (opcodes other than PUSH): %s", txin.scriptSig.ToString().c_str());
     }
     BOOST_FOREACH(const CTxOut& txout, vout)
         if (!::IsStandard(txout.scriptPubKey))
@@ -275,7 +274,7 @@ bool CTransaction::IsStandard() const
 // 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
+bool CTransaction::AreInputsStandard(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const
 {
     if (fTestNet)
         return true; // Allow non-standard on testnet
@@ -285,20 +284,23 @@ bool CTransaction::IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTrans
         COutPoint prevout = vin[i].prevout;
         assert(mapInputs.count(prevout.hash) > 0);
         CTransaction& txPrev = mapInputs[prevout.hash].second;
+        assert(prevout.n < txPrev.vout.size());
 
         vector<vector<unsigned char> > vSolutions;
-        txntype whichType;
-        if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions))
-            return false;
+        txnouttype whichType;
+        // get the scriptPubKey corresponding to this input:
+        CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey;
+        if (!Solver(prevScript, whichType, vSolutions))
+            return error("nonstandard txin (spending nonstandard txout %s)", prevScript.ToString().c_str());
         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())))
+            if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, nUnused))
                 return false;
+            CScript subscript(stack.back().begin(), stack.back().end());
+            if (!::IsStandard(subscript))
+                return error("nonstandard txin (nonstandard OP_EVAL subscript %s)", subscript.ToString().c_str());
         }
     }
 
@@ -425,7 +427,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
         return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx"));
 
     // To help v0.1.5 clients who would see it as a negative number
-    if ((int64)nLockTime > INT_MAX)
+    if ((int64)nLockTime > std::numeric_limits<int>::max())
         return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
 
     // Rather not work on nonstandard transactions (unless -testnet)
@@ -481,7 +483,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
         }
 
         // Check for non-standard OP_EVALs in inputs
-        if (!IsStandardInputs(mapInputs))
+        if (!AreInputsStandard(mapInputs))
             return error("AcceptToMemoryPool() : nonstandard transaction input");
 
         // Check against previous transactions
@@ -501,7 +503,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
             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))
+        if (nFees < GetMinFee(1000, true, GMF_RELAY))
             return error("AcceptToMemoryPool() : not enough fees");
 
         // Continuously rate-limit free transactions
@@ -824,7 +826,7 @@ int GetNumBlocksOfPeers()
 
 bool IsInitialBlockDownload()
 {
-    if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold))
+    if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
         return true;
     static int64 nLastUpdate;
     static CBlockIndex* pindexLastBest;
@@ -900,7 +902,7 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTes
 {
     if (IsCoinBase())
         return true; // Coinbase transactions have no inputs to fetch.
-    
+
     for (int i = 0; i < vin.size(); i++)
     {
         COutPoint prevout = vin[i].prevout;
@@ -944,6 +946,17 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTes
                 return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
         }
     }
+
+    // Make sure all prevout.n's are valid:
+    for (int i = 0; i < vin.size(); i++)
+    {
+        const COutPoint prevout = vin[i].prevout;
+        const CTxIndex& txindex = inputsRet[prevout.hash].first;
+        const CTransaction& txPrev = inputsRet[prevout.hash].second;
+        if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
+            return DoS(100, error("FetchInputs() : %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()));
+    }
+
     return true;
 }
 
@@ -974,13 +987,31 @@ bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inp
                     if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
                         return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
 
-            // Skip ECDSA signature verification when connecting blocks (fBlock=true) during initial download
-            // (before the last blockchain checkpoint). This is safe because block merkle hashes are
+            // Skip ECDSA signature verification when connecting blocks (fBlock=true)
+            // before the last blockchain checkpoint. This is safe because block merkle hashes are
             // still computed and checked, and any change will be caught at the next checkpoint.
-            if (!(fBlock && IsInitialBlockDownload()))
+            if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
+            {
+                bool fStrictOpEval = true;
+                // This code should be removed when OP_EVAL has
+                // a majority of hashing power on the network.
+                if (fBlock)
+                {
+                    // To avoid being on the short end of a block-chain split,
+                    // interpret OP_EVAL as a NO_OP until blocks with timestamps
+                    // after opevaltime:
+                    int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); // Feb 1, 2012
+                    fStrictOpEval = (pindexBlock->nTime >= nEvalSwitchTime);
+                }
+                // if !fBlock, then always be strict-- don't accept
+                // invalid-under-new-rules OP_EVAL transactions into
+                // our memory pool (don't relay them, don't include them
+                // in blocks we mine).
+
                 // Verify signature
-                if (!VerifySignature(txPrev, *this, i, nSigOpsRet))
+                if (!VerifySignature(txPrev, *this, i, nSigOpsRet, fStrictOpEval))
                     return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+            }
 
             // Check for conflicts (double-spend)
             // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
@@ -1054,7 +1085,7 @@ bool CTransaction::ClientConnectInputs()
 
             // Verify signature
             int nUnused = 0;
-            if (!VerifySignature(txPrev, *this, i, nUnused))
+            if (!VerifySignature(txPrev, *this, i, nUnused, false))
                 return error("ConnectInputs() : VerifySignature failed");
 
             ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@@ -1900,7 +1931,7 @@ unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };
 
 bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
 {
-    static map<unsigned int, vector<unsigned char> > mapReuseKey;
+    static map<CService, vector<unsigned char> > mapReuseKey;
     RandAddSeedPerfmon();
     if (fDebug) {
         printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
@@ -1956,7 +1987,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
 
         pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
 
-        AddTimeData(pfrom->addr.ip, nTime);
+        AddTimeData(pfrom->addr, nTime);
 
         // Change version
         if (pfrom->nVersion >= 209)
@@ -1984,7 +2015,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
         }
 
         // Ask the first connected node for block updates
-        static int nAskedForBlocks;
+        static int nAskedForBlocks = 0;
         if (!pfrom->fClient &&
             (pfrom->nVersion < 32000 || pfrom->nVersion >= 32400) &&
              (nAskedForBlocks < 1 || vNodes.size() <= 1))
@@ -2062,7 +2093,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                     static uint256 hashSalt;
                     if (hashSalt == 0)
                         RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt));
-                    uint256 hashRand = hashSalt ^ (((int64)addr.ip)<<32) ^ ((GetTime()+addr.ip)/(24*60*60));
+                    int64 hashAddr = addr.GetHash();
+                    uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60));
                     hashRand = Hash(BEGIN(hashRand), END(hashRand));
                     multimap<uint256, CNode*> mapMix;
                     BOOST_FOREACH(CNode* pnode, vNodes)
@@ -2361,12 +2393,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
         /// we have a chance to check the order here
 
         // Keep giving the same key to the same ip until they use it
-        if (!mapReuseKey.count(pfrom->addr.ip))
-            pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr.ip], true);
+        if (!mapReuseKey.count(pfrom->addr))
+            pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true);
 
         // Send back approval of order and pubkey to use
         CScript scriptPubKey;
-        scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG;
+        scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG;
         pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey);
     }
 
@@ -2939,7 +2971,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
 
             // Transaction fee required depends on block size
             bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
-            int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true);
+            int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
 
             // Connecting shouldn't fail due to dependency on other memory pool transactions
             // because we're already processing them in order of dependency