diff options
author | Gavin Andresen <gavinandresen@gmail.com> | 2011-12-20 14:43:31 -0500 |
---|---|---|
committer | Gavin Andresen <gavinandresen@gmail.com> | 2011-12-20 14:43:31 -0500 |
commit | 595925592d36fb5d5d34beea3c3e71fca2b6726e (patch) | |
tree | e28c34353aa08a6f3c0368a39982970c08245ea3 /src/main.cpp | |
parent | f06e3e0ea6c8da90585a9f0936c390659dcece37 (diff) | |
parent | 77f21f1583deb89bf3fffe80fe9b181fedb1dd60 (diff) |
Merge branch 'op_eval'
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 257 |
1 files changed, 198 insertions, 59 deletions
diff --git a/src/main.cpp b/src/main.cpp index e2f3cc9f84..cfd6ac2c46 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -246,6 +246,67 @@ 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 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() > 500) + return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size()); + if (!txin.scriptSig.IsPushOnly()) + return error("nonstandard txin (opcodes other than PUSH): %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::AreInputsStandard(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; + 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, 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()); + } + } + + return true; +} + int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -369,15 +430,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi if ((int64)nLockTime > std::numeric_limits<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 +473,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 (!AreInputsStandard(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 +897,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 +963,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())); @@ -890,9 +980,27 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo // (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())) + { + 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)) + 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 @@ -965,7 +1073,8 @@ bool CTransaction::ClientConnectInputs() return false; // Verify signature - if (!VerifySignature(txPrev, *this, i)) + int nUnused = 0; + 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 @@ -1023,14 +1132,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 +1407,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 +2956,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 +2964,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); @@ -2887,6 +3019,13 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& } ++nExtraNonce; pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce); + + // Put "OP_EVAL" in the coinbase so everybody can tell when + // a majority of miners support it + const char* pOpEvalName = GetOpName(OP_EVAL); + pblock->vtx[0].vin[0].scriptSig += CScript() << std::vector<unsigned char>(pOpEvalName, pOpEvalName+strlen(pOpEvalName)); + assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } |