diff options
author | Johnson Lau <jl2012@xbt.hk> | 2016-10-16 23:53:16 +0800 |
---|---|---|
committer | Johnson Lau <jl2012@xbt.hk> | 2016-10-16 23:53:35 +0800 |
commit | 3ade2f64cfe43ab53e4869ffc35d5fd23201e1c1 (patch) | |
tree | 78eff29685321a56c9456c5d457c4c079aa7ddb9 | |
parent | 03dd707dc027fbf6f24120213f8eb66571600374 (diff) |
Add standard limits for P2WSH with tests
-rwxr-xr-x | qa/rpc-tests/p2p-segwit.py | 111 | ||||
-rw-r--r-- | src/main.cpp | 6 | ||||
-rw-r--r-- | src/policy/policy.cpp | 54 | ||||
-rw-r--r-- | src/policy/policy.h | 12 |
4 files changed, 181 insertions, 2 deletions
diff --git a/qa/rpc-tests/p2p-segwit.py b/qa/rpc-tests/p2p-segwit.py index c2ea20bb84..16d1e86802 100755 --- a/qa/rpc-tests/p2p-segwit.py +++ b/qa/rpc-tests/p2p-segwit.py @@ -1706,6 +1706,116 @@ class SegWitTest(BitcoinTestFramework): assert(block_version & (1 << VB_WITNESS_BIT) != 0) self.nodes[0].setmocktime(0) # undo mocktime + def test_non_standard_witness(self): + print("\tTesting detection of non-standard P2WSH witness") + pad = chr(1).encode('latin-1') + + # Create scripts for tests + scripts = [] + scripts.append(CScript([OP_DROP] * 100)) + scripts.append(CScript([OP_DROP] * 99)) + scripts.append(CScript([pad * 59] * 59 + [OP_DROP] * 60)) + scripts.append(CScript([pad * 59] * 59 + [OP_DROP] * 61)) + + p2wsh_scripts = [] + + assert(len(self.utxo)) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + + # For each script, generate a pair of P2WSH and P2SH-P2WSH output. + outputvalue = (self.utxo[0].nValue - 1000) // (len(scripts) * 2) + for i in scripts: + p2wsh = CScript([OP_0, sha256(i)]) + p2sh = hash160(p2wsh) + p2wsh_scripts.append(p2wsh) + tx.vout.append(CTxOut(outputvalue, p2wsh)) + tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL]))) + tx.rehash() + txid = tx.sha256 + self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True) + + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # Creating transactions for tests + p2wsh_txs = [] + p2sh_txs = [] + for i in range(len(scripts)): + p2wsh_tx = CTransaction() + p2wsh_tx.vin.append(CTxIn(COutPoint(txid,i*2))) + p2wsh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))]))) + p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) + p2wsh_tx.rehash() + p2wsh_txs.append(p2wsh_tx) + p2sh_tx = CTransaction() + p2sh_tx.vin.append(CTxIn(COutPoint(txid,i*2+1), CScript([p2wsh_scripts[i]]))) + p2sh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))]))) + p2sh_tx.wit.vtxinwit.append(CTxInWitness()) + p2sh_tx.rehash() + p2sh_txs.append(p2sh_tx) + + # Testing native P2WSH + # Witness stack size, excluding witnessScript, over 100 is non-standard + p2wsh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]] + self.std_node.test_transaction_acceptance(p2wsh_txs[0], True, False, b'bad-witness-nonstandard') + # Non-standard nodes should accept + self.test_node.test_transaction_acceptance(p2wsh_txs[0], True, True) + + # Stack element size over 80 bytes is non-standard + p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]] + # It can't be used to blind a node to the transaction + self.std_node.announce_tx_and_wait_for_getdata(p2wsh_txs[1]) + self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, False, b'bad-witness-nonstandard') + self.std_node.announce_tx_and_wait_for_getdata(p2wsh_txs[1]) + self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, False, b'bad-witness-nonstandard') + # Non-standard nodes should accept + self.test_node.test_transaction_acceptance(p2wsh_txs[1], True, True) + # Standard nodes should accept if element size is not over 80 bytes + p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]] + self.std_node.announce_tx_and_wait_for_getdata(p2wsh_txs[1]) + self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, True) + + # witnessScript size at 3600 bytes is standard + p2wsh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]] + self.test_node.test_transaction_acceptance(p2wsh_txs[2], True, True) + self.std_node.test_transaction_acceptance(p2wsh_txs[2], True, True) + + # witnessScript size at 3601 bytes is non-standard + p2wsh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]] + self.std_node.test_transaction_acceptance(p2wsh_txs[3], True, False, b'bad-witness-nonstandard') + # Non-standard nodes should accept + self.test_node.test_transaction_acceptance(p2wsh_txs[3], True, True) + + # Repeating the same tests with P2SH-P2WSH + p2sh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]] + self.std_node.test_transaction_acceptance(p2sh_txs[0], True, False, b'bad-witness-nonstandard') + self.test_node.test_transaction_acceptance(p2sh_txs[0], True, True) + p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]] + self.std_node.announce_tx_and_wait_for_getdata(p2sh_txs[1]) + self.std_node.test_transaction_acceptance(p2sh_txs[1], True, False, b'bad-witness-nonstandard') + self.std_node.announce_tx_and_wait_for_getdata(p2sh_txs[1]) + self.std_node.test_transaction_acceptance(p2sh_txs[1], True, False, b'bad-witness-nonstandard') + self.test_node.test_transaction_acceptance(p2sh_txs[1], True, True) + p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]] + self.std_node.announce_tx_and_wait_for_getdata(p2sh_txs[1]) + self.std_node.test_transaction_acceptance(p2sh_txs[1], True, True) + p2sh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]] + self.test_node.test_transaction_acceptance(p2sh_txs[2], True, True) + self.std_node.test_transaction_acceptance(p2sh_txs[2], True, True) + p2sh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]] + self.std_node.test_transaction_acceptance(p2sh_txs[3], True, False, b'bad-witness-nonstandard') + self.test_node.test_transaction_acceptance(p2sh_txs[3], True, True) + + self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node + # Valid but non-standard transactions in a block should be accepted by standard node + sync_blocks(self.nodes) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + assert_equal(len(self.nodes[1].getrawmempool()), 0) + + self.utxo.pop(0) + + def run_test(self): # Setup the p2p connections and start up the network thread. self.test_node = TestNode() # sets NODE_WITNESS|NODE_NETWORK @@ -1778,6 +1888,7 @@ class SegWitTest(BitcoinTestFramework): self.test_segwit_versions() self.test_premature_coinbase_witness_spend() self.test_signature_version_1() + self.test_non_standard_witness() sync_blocks(self.nodes) if self.test_upgrade: self.test_upgrade_after_activation(self.nodes[2], 2) diff --git a/src/main.cpp b/src/main.cpp index c92a38be98..020378b5dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -1273,6 +1273,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (fRequireStandard && !AreInputsStandard(tx, view)) return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); + // Check for non-standard witness in P2WSH + if (!tx.wit.IsNull() && fRequireStandard && !IsWitnessStandard(tx, view)) + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true); + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); CAmount nValueOut = tx.GetValueOut(); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 48080abc77..ae42b2bd74 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin developers +// Copyright (c) 2009-2016 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -154,6 +154,58 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return true; } +bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +{ + if (tx.IsCoinBase()) + return true; // Coinbases are skipped + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + // We don't care if witness for this input is empty, since it must not be bloated. + // If the script is invalid without witness, it would be caught sooner or later during validation. + if (tx.wit.vtxinwit[i].IsNull()) + continue; + + const CTxOut &prev = mapInputs.GetOutputFor(tx.vin[i]); + + // get the scriptPubKey corresponding to this input: + CScript prevScript = prev.scriptPubKey; + + if (prevScript.IsPayToScriptHash()) { + std::vector <std::vector<unsigned char> > stack; + // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig + // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway. + // If the check fails at this stage, we know that this txid must be a bad one. + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE)) + return false; + if (stack.empty()) + return false; + prevScript = CScript(stack.back().begin(), stack.back().end()); + } + + int witnessversion = 0; + std::vector<unsigned char> witnessprogram; + + // Non-witness program must not be associated with any witness + if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram)) + return false; + + // Check P2WSH standard limits + if (witnessversion == 0 && witnessprogram.size() == 32) { + if (tx.wit.vtxinwit[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) + return false; + size_t sizeWitnessStack = tx.wit.vtxinwit[i].scriptWitness.stack.size() - 1; + if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS) + return false; + for (unsigned int j = 0; j < sizeWitnessStack; j++) { + if (tx.wit.vtxinwit[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE) + return false; + } + } + } + return true; +} + unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost) diff --git a/src/policy/policy.h b/src/policy/policy.h index 9d6ff1233b..fb528d7486 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -30,6 +30,12 @@ static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5; static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; /** Default for -bytespersigop */ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; +/** The maximum number of witness stack items in a standard P2WSH script */ +static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; +/** The maximum size of each witness stack item in a standard P2WSH script */ +static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; +/** The maximum size of a standard witnessScript */ +static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; /** * Standard script verification flags that standard transactions will comply * with. However scripts violating these flags may still be present in valid @@ -69,6 +75,12 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes * @return True if all inputs (scriptSigs) use only standard transaction forms */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); + /** + * Check if the transaction is over standard P2WSH resources limit: + * 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements + * These limits are adequate for multi-signature up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL, + */ +bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); extern unsigned int nBytesPerSigOp; |