diff --git a/src/init.cpp b/src/init.cpp index f09f044..1045610 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -496,21 +496,21 @@ bool AppInit2(int argc, char* argv[]) fNoListen = !GetBoolArg("-listen", true); // This code can be removed once a super-majority of the network has upgraded. - if (GetBoolArg("-bip16", true)) + if (GetBoolArg("-bip17", true)) { if (fTestNet) - SoftSetArg("-paytoscripthashtime", "1329264000"); // Feb 15 + SoftSetArg("-p2shtime", "1329264000"); // Feb 15 else - SoftSetArg("-paytoscripthashtime", "1330578000"); // Mar 1 + SoftSetArg("-p2shtime", "1332460800"); // Mar 23 // Put "/P2SH/" in the coinbase so everybody can tell when // a majority of miners support it - const char* pszP2SH = "/P2SH/"; + const char* pszP2SH = "p2sh/CHV"; COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); } else { - const char* pszP2SH = "NOP2SH"; + const char* pszP2SH = "p2sh/NOCHV"; COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); } diff --git a/src/main.cpp b/src/main.cpp index b73037f..7d5c4f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -260,7 +260,7 @@ bool CTransaction::IsStandard() const // ~65-byte public keys, plus a few script ops. if (txin.scriptSig.size() > 500) return false; - if (!txin.scriptSig.IsPushOnly()) + if (!::IsStandardInput(txin.scriptSig)) return false; } BOOST_FOREACH(const CTxOut& txout, vout) @@ -270,16 +270,16 @@ bool CTransaction::IsStandard() const } // -// Check transaction inputs, and make sure any -// pay-to-script-hash transactions are evaluating IsStandard scripts +// Check transaction inputs, and make sure their scriptPubKeys are "standard" // -// Why bother? To avoid denial-of-service attacks; an attacker -// can submit a standard HASH... OP_EQUAL transaction, -// which will get accepted into blocks. The redemption -// script can be anything; an attacker could use a very -// expensive-to-check-upon-redemption script like: +// Why bother? To avoid denial-of-service attacks; an attacker can mine a +// non-standard transaction with an expensive scriptPubKey, and then use a +// standard transaction to trick a miner into executing it. For example: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // +// Note that this does NOT check scriptSig, as that is already done execution +// for the inputs. +// bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const { if (IsCoinBase()) @@ -298,28 +298,12 @@ bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig the - // IsStandard() call returns false + // non-standard. vector > stack; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) + std::vector vchLastScript; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, vchLastScript)) return false; - if (whichType == TX_SCRIPTHASH) - { - if (stack.empty()) - return false; - CScript subscript(stack.back().begin(), stack.back().end()); - vector > vSolutions2; - txnouttype whichType2; - if (!Solver(subscript, whichType2, vSolutions2)) - return false; - if (whichType2 == TX_SCRIPTHASH) - return false; - nArgsExpected += ScriptSigArgsExpected(whichType2, vSolutions2); - } - if (stack.size() != nArgsExpected) return false; } @@ -1086,15 +1070,14 @@ int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const for (int i = 0; i < vin.size(); i++) { const CTxOut& prevout = GetOutputFor(vin[i], inputs); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); + nSigOps += prevout.scriptPubKey.GetSigOpCount(true); } return nSigOps; } bool CTransaction::ConnectInputs(MapPrevTx inputs, map& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner) { // 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 @@ -1131,6 +1114,20 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return DoS(100, error("ConnectInputs() : txin values out of range")); + bool fStrictPayToScriptHash = true; + if (fBlock) + { + // To avoid being on the short end of a block-chain split, + // don't do validation of pay-to-script-hash transactions + // until blocks with timestamps after p2shtime: + int64 nP2SHSwitchTime = GetArg("-p2shtime", std::numeric_limits::max()); + fStrictPayToScriptHash = (pindexBlock->nTime >= nP2SHSwitchTime); + } + // if !fBlock, then always be strict-- don't accept + // invalid-under-new-rules pay-to-script-hash transactions into + // our memory pool (don't relay them, don't include them + // in blocks we mine). + // 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. @@ -1241,13 +1238,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) if (!CheckBlock()) return false; - // To avoid being on the short end of a block-chain split, - // don't do secondary validation of pay-to-script-hash transactions - // until blocks with timestamps after paytoscripthashtime (see init.cpp for default). - // This code can be removed once a super-majority of the network has upgraded. - int64 nEvalSwitchTime = GetArg("-paytoscripthashtime", std::numeric_limits::max()); - bool fStrictPayToScriptHash = (pindex->nTime >= nEvalSwitchTime); - //// issue here: it doesn't know the version unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); @@ -1270,19 +1260,9 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) return false; - if (fStrictPayToScriptHash) - { - // Add in sigops done by pay-to-script-hash inputs; - // this is to prevent a "rogue miner" from creating - // an incredibly-expensive-to-validate block. - nSigOps += tx.GetP2SHSigOpCount(mapInputs); - if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); - } - nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); - if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) + if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false)) return false; } diff --git a/src/main.h b/src/main.h index d9f976c..200e56a 100644 --- a/src/main.h +++ b/src/main.h @@ -696,12 +696,11 @@ public: @param[in] pindexBlock @param[in] fBlock true if called from ConnectBlock @param[in] fMiner true if called from CreateNewBlock - @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions @return Returns true if all checks succeed */ bool ConnectInputs(MapPrevTx inputs, std::map& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true); + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner); bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); diff --git a/src/script.cpp b/src/script.cpp index b6f1202..23610e6 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -205,7 +205,7 @@ const char* GetOpName(opcodetype opcode) // expanson case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; + case OP_CHECKHASHVERIFY : return "OP_CHECKHASHVERIFY"; case OP_NOP3 : return "OP_NOP3"; case OP_NOP4 : return "OP_NOP4"; case OP_NOP5 : return "OP_NOP5"; @@ -218,6 +218,7 @@ const char* GetOpName(opcodetype opcode) // template matching params + case OP_SCRIPTHASH : return "OP_SCRIPTHASH"; case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEY : return "OP_PUBKEY"; @@ -227,7 +228,7 @@ const char* GetOpName(opcodetype opcode) } } -bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, bool fValidatePayToScriptHash, std::vector &vchLastScript) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -312,7 +313,7 @@ bool EvalScript(vector >& 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_NOP1: 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; @@ -928,6 +929,36 @@ bool EvalScript(vector >& stack, const CScript& script, co } break; + case OP_CHECKHASHVERIFY: + { + // This was OP_NOP2 in old scripts + if (!fValidatePayToScriptHash) + break; + + if (stack.size() < 1) + return false; + + // The only stack item here is what we expect the code to hash to + valtype& vchHashCheck = stacktop(-1); + + // This hashes the code with RIPEMD160(SHA256(vchLastScript)) + valtype vchHash(20); + { + uint160 hash160 = Hash160(vchLastScript); + memcpy(&vchHash[0], &hash160, sizeof(hash160)); + } + + bool fEqual = (vchHash == vchHashCheck); + + // Don't pop anything off the stack, for backward compatibility + + if (!fEqual) + // If the hash doesn't match, abort + return false; + // If the hash matches, we're good: act like OP_NOP2 for compatibility + } + break; + case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: { @@ -1009,6 +1040,9 @@ bool EvalScript(vector >& stack, const CScript& script, co if (stack.size() + altstack.size() > 1000) return false; } + + // Save the last code segment (from the last executed OP_CODESEPARATOR onward) + vchLastScript = std::vector(pbegincodehash, pend); } catch (...) { @@ -1137,16 +1171,9 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); - vSolutionsRet.push_back(hashBytes); - return true; + // Push-to-script-hash txn, sender provides hash of verification script, receiver provides script + mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_SCRIPTHASH << OP_CHECKHASHVERIFY << OP_DROP)); } // Scan templates @@ -1221,6 +1248,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector& pubkeys, const CKeyStore& keystore) { @@ -1470,37 +1534,15 @@ bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + vector > stack; + std::vector vchLastScript; + if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, fValidatePayToScriptHash, vchLastScript)) return false; - if (fValidatePayToScriptHash) - stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, fValidatePayToScriptHash, vchLastScript)) return false; if (stack.empty()) return false; - - if (CastToBool(stack.back()) == false) - return false; - - // Additional validation for spend-to-script-hash transactions: - if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash()) - { - if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only - return false; // or validation fails - - const valtype& pubKeySerialized = stackCopy.back(); - CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); - popstack(stackCopy); - - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType)) - return false; - if (stackCopy.empty()) - return false; - return CastToBool(stackCopy.back()); - } - - return true; + return CastToBool(stack.back()); } @@ -1521,9 +1563,8 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans if (whichType == TX_SCRIPTHASH) { - // Solver returns the subscript that need to be evaluated; - // the final scriptSig is the signatures from that - // and then the serialized subscript: + // Solver returns the end of the scriptSig; the final scriptSig is the + // signatures for that and then this end script: CScript subscript = txin.scriptSig; // Recompute txn hash using subscript in place of scriptPubKey: @@ -1533,7 +1574,8 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans return false; if (subType == TX_SCRIPTHASH) return false; - txin.scriptSig << static_cast(subscript); // Append serialized subscript + txin.scriptSig << OP_CODESEPARATOR; + txin.scriptSig += subscript; } // Test solution @@ -1585,44 +1627,11 @@ int CScript::GetSigOpCount(bool fAccurate) const return n; } -int CScript::GetSigOpCount(const CScript& scriptSig) const -{ - if (!IsPayToScriptHash()) - return GetSigOpCount(true); - - // This is a pay-to-script-hash scriptPubKey; - // get the last item that the scriptSig - // pushes onto the stack: - const_iterator pc = scriptSig.begin(); - vector data; - while (pc < scriptSig.end()) - { - opcodetype opcode; - if (!scriptSig.GetOp(pc, opcode, data)) - return 0; - if (opcode > OP_16) - return 0; - } - - /// ... and return it's opcount: - CScript subscript(data.begin(), data.end()); - return subscript.GetSigOpCount(true); -} - -bool CScript::IsPayToScriptHash() const -{ - // Extra-fast test for pay-to-script-hash CScripts: - return (this->size() == 23 && - this->at(0) == OP_HASH160 && - this->at(1) == 0x14 && - this->at(22) == OP_EQUAL); -} - void CScript::SetBitcoinAddress(const CBitcoinAddress& address) { this->clear(); if (address.IsScript()) - *this << OP_HASH160 << address.GetHash160() << OP_EQUAL; + *this << address.GetHash160() << OP_CHECKHASHVERIFY << OP_DROP; else *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG; } @@ -1642,5 +1651,5 @@ void CScript::SetPayToScriptHash(const CScript& subscript) assert(!subscript.empty()); uint160 subscriptHash = Hash160(subscript); this->clear(); - *this << OP_HASH160 << subscriptHash << OP_EQUAL; + *this << subscriptHash << OP_CHECKHASHVERIFY << OP_DROP; } diff --git a/src/script.h b/src/script.h index 46f2d31..e3aa651 100644 --- a/src/script.h +++ b/src/script.h @@ -160,7 +160,7 @@ enum opcodetype // expansion OP_NOP1, - OP_NOP2, + OP_CHECKHASHVERIFY, // "crypto" OP_NOP3, OP_NOP4, OP_NOP5, @@ -175,6 +175,7 @@ enum opcodetype // template matching params OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, + OP_SCRIPTHASH = 0xfc, OP_PUBKEYHASH = 0xfd, OP_PUBKEY = 0xfe, @@ -489,12 +490,6 @@ public: // ... OP_N CHECKMULTISIG ... int GetSigOpCount(bool fAccurate) const; - // Accurately count sigOps, including sigOps in - // pay-to-script-hash transactions: - int GetSigOpCount(const CScript& scriptSig) const; - - bool IsPayToScriptHash() const; - // Called by CTransaction::IsStandard bool IsPushOnly() const { @@ -558,10 +553,11 @@ public: -bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType); +bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, bool fValidatePayToScriptHash, std::vector &vchLastScript); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); bool IsStandard(const CScript& scriptPubKey); +bool IsStandardInput(const CScript& scriptSig); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet); bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index aa72c00..815311a 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -49,8 +49,8 @@ BOOST_AUTO_TEST_SUITE(script_P2SH_tests) BOOST_AUTO_TEST_CASE(sign) { // Pay-to-script-hash looks like this: - // scriptSig: - // scriptPubKey: HASH160 EQUAL + // scriptSig: OP_CODESEPARATOR