Skip to content

Commit

Permalink
Merge pull request bitcoin#3843
Browse files Browse the repository at this point in the history
787ee0c Check redeemScript size does not exceed 520 byte limit (Peter Todd)
4d79098 Increase IsStandard() scriptSig length (Peter Todd)
f80cffa Do not trigger a DoS ban if SCRIPT_VERIFY_NULLDUMMY fails (Peter Todd)
6380180 Add rejection of non-null CHECKMULTISIG dummy values (Peter Todd)
29c1749 Let tx (in)valid tests use any SCRIPT_VERIFY flag (Peter Todd)
68f7d1d Create (MANDATORY|STANDARD)_SCRIPT_VERIFY_FLAGS constants (Peter Todd)
  • Loading branch information
laanwj committed May 9, 2014
2 parents 1c0319b + 787ee0c commit 54f1022
Show file tree
Hide file tree
Showing 12 changed files with 211 additions and 71 deletions.
3 changes: 3 additions & 0 deletions src/keystore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)

bool CBasicKeyStore::AddCScript(const CScript& redeemScript)
{
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
return error("CBasicKeyStore::AddCScript() : redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);

LOCK(cs_KeyStore);
mapScripts[redeemScript.GetID()] = redeemScript;
return true;
Expand Down
38 changes: 27 additions & 11 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,14 @@ bool IsStandardTx(const CTransaction& tx, string& reason)

BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
// pay-to-script-hash, which is 3 ~80-byte signatures, 3
// ~65-byte public keys, plus a few script ops.
if (txin.scriptSig.size() > 500) {
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys. (remember the 520 byte limit on redeemScript size) That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20
// CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
// considered standard)
if (txin.scriptSig.size() > 1650) {
reason = "scriptsig-size";
return false;
}
Expand Down Expand Up @@ -945,7 +949,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa

// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC))
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS))
{
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
}
Expand Down Expand Up @@ -1588,14 +1592,26 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
} else if (!check()) {
if (flags & SCRIPT_VERIFY_STRICTENC) {
// For now, check whether the failure was caused by non-canonical
// encodings or not; if so, don't trigger DoS protection.
CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0);
if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
// Check whether the failure was caused by a
// non-mandatory script verification check, such as
// non-standard DER encodings or non-null dummy
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
CScriptCheck check(coins, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, 0);
if (check())
return state.Invalid(false, REJECT_NONSTANDARD, "non-canonical");
return state.Invalid(false, REJECT_NONSTANDARD, "non-mandatory-script-verify-flag");
}
return state.DoS(100,false, REJECT_NONSTANDARD, "non-canonical");
// Failures of other flags indicate a transaction that is
// invalid in new blocks, e.g. a invalid P2SH. We DoS ban
// such nodes as they are not following the protocol. That
// said during an upgrade careful thought should be taken
// as to the correct behavior - we may want to continue
// peering with non-upgraded nodes even after a soft-fork
// super-majority vote has passed.
return state.DoS(100,false, REJECT_INVALID, "mandatory-script-verify-flag-failed");
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ inline bool AllowFree(double dPriority)
// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
// instead of being performed inline.
bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true,
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC,
unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS,
std::vector<CScriptCheck> *pvChecks = NULL);

// Apply the effects of this transaction on the UTXO set represented by view
Expand Down
5 changes: 4 additions & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,11 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;

// Note that flags: we don't want to set mempool/IsStandard()
// policy here, but we still have to ensure that the block we
// create only contains transactions that are valid in new blocks.
CValidationState state;
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH))
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS))
continue;

CTxUndo txundo;
Expand Down
9 changes: 7 additions & 2 deletions src/rpcmisc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Value validateaddress(const Array& params, bool fHelp)
//
// Used by addmultisigaddress / createmultisig:
//
CScript _createmultisig(const Array& params)
CScript _createmultisig_redeemScript(const Array& params)
{
int nRequired = params[0].get_int();
const Array& keys = params[1].get_array();
Expand Down Expand Up @@ -228,6 +228,11 @@ CScript _createmultisig(const Array& params)
}
CScript result;
result.SetMultisig(nRequired, pubkeys);

if (result.size() > MAX_SCRIPT_ELEMENT_SIZE)
throw runtime_error(
strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE));

return result;
}

Expand Down Expand Up @@ -263,7 +268,7 @@ Value createmultisig(const Array& params, bool fHelp)
}

// Construct using pay-to-script-hash:
CScript inner = _createmultisig(params);
CScript inner = _createmultisig_redeemScript(params);
CScriptID innerID = inner.GetID();
CBitcoinAddress address(innerID);

Expand Down
2 changes: 1 addition & 1 deletion src/rpcrawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
{
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0))
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS, 0))
fComplete = false;
}

Expand Down
4 changes: 2 additions & 2 deletions src/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ Value sendmany(const Array& params, bool fHelp)
}

// Defined in rpcmisc.cpp
extern CScript _createmultisig(const Array& params);
extern CScript _createmultisig_redeemScript(const Array& params);

Value addmultisigaddress(const Array& params, bool fHelp)
{
Expand Down Expand Up @@ -908,7 +908,7 @@ Value addmultisigaddress(const Array& params, bool fHelp)
strAccount = AccountFromValue(params[2]);

// Construct using pay-to-script-hash:
CScript inner = _createmultisig(params);
CScript inner = _createmultisig_redeemScript(params);
CScriptID innerID = inner.GetID();
pwalletMain->AddCScript(inner);

Expand Down
18 changes: 16 additions & 2 deletions src/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,8 +923,22 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
fSuccess = false;
}

while (i-- > 0)
// Clean up stack of actual arguments
while (i-- > 1)
popstack(stack);

// A bug causes CHECKMULTISIG to consume one extra argument
// whose contents were not checked in any way.
//
// Unfortunately this is a potential source of mutability,
// so optionally verify it is exactly equal to zero prior
// to removing it from the stack.
if (stack.size() < 1)
return false;
if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size())
return error("CHECKMULTISIG dummy argument not null");
popstack(stack);

stack.push_back(fSuccess ? vchTrue : vchFalse);

if (opcode == OP_CHECKMULTISIGVERIFY)
Expand Down Expand Up @@ -1659,7 +1673,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa
}

// Test solution
return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0);
return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS, 0);
}

bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType)
Expand Down
20 changes: 20 additions & 0 deletions src/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,28 @@ enum
SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
SCRIPT_VERIFY_EVEN_S = (1U << 2), // enforce even S values in signatures (depends on STRICTENC)
SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
};

// Mandatory script verification flags that all new blocks must comply with for
// them to be valid. (but old blocks may not comply with) Currently just P2SH,
// but in the future other flags may be added, such as a soft-fork to enforce
// strict DER encoding.
//
// Failing one of these tests may trigger a DoS ban - see CheckInputs() for
// details.
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;

// Standard script verification flags that standard transactions will comply
// with. However scripts violating these flags may still be present in valid
// blocks and we must accept those blocks.
static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_STRICTENC |
SCRIPT_VERIFY_NULLDUMMY;

// For convenience, standard but not mandatory verify flags.
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;

enum txnouttype
{
TX_NONSTANDARD,
Expand Down
Loading

0 comments on commit 54f1022

Please sign in to comment.