diff --git a/src/script/standard.h b/src/script/standard.h --- a/src/script/standard.h +++ b/src/script/standard.h @@ -43,7 +43,9 @@ */ static const uint32_t MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC | - SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_NULLFAIL; + SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_VERIFY_LOW_S | + SCRIPT_VERIFY_NULLFAIL | SCRIPT_ENABLE_CHECKDATASIG | + SCRIPT_VERIFY_SIGPUSHONLY | SCRIPT_VERIFY_CLEANSTACK; enum txnouttype { TX_NONSTANDARD, diff --git a/src/test/checkdatasig_tests.cpp b/src/test/checkdatasig_tests.cpp --- a/src/test/checkdatasig_tests.cpp +++ b/src/test/checkdatasig_tests.cpp @@ -19,7 +19,8 @@ BOOST_FIXTURE_TEST_SUITE(checkdatasig_tests, BasicTestingSetup) std::array flagset{ - {0, STANDARD_SCRIPT_VERIFY_FLAGS, MANDATORY_SCRIPT_VERIFY_FLAGS}}; + {0, STANDARD_SCRIPT_VERIFY_FLAGS &(~SCRIPT_ENABLE_CHECKDATASIG), + MANDATORY_SCRIPT_VERIFY_FLAGS &(~SCRIPT_ENABLE_CHECKDATASIG)}}; const uint8_t vchPrivkey[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -29,7 +29,8 @@ void CheckScriptSigOps(const CScript &script, uint32_t accurate_sigops, uint32_t inaccurate_sigops, uint32_t datasigops) { - const uint32_t stdflags = STANDARD_SCRIPT_VERIFY_FLAGS; + const uint32_t stdflags = + STANDARD_SCRIPT_VERIFY_FLAGS & (~SCRIPT_ENABLE_CHECKDATASIG); const uint32_t datasigflags = STANDARD_CHECKDATASIG_VERIFY_FLAGS; BOOST_CHECK_EQUAL(script.GetSigOpCount(stdflags, false), inaccurate_sigops); diff --git a/test/functional/abc-mempool-accept-txn.py b/test/functional/abc-mempool-accept-txn.py --- a/test/functional/abc-mempool-accept-txn.py +++ b/test/functional/abc-mempool-accept-txn.py @@ -10,7 +10,7 @@ from test_framework.test_framework import ComparisonTestFramework from test_framework.util import (assert_raises_rpc_error, assert_equal) -from test_framework.comptool import TestManager, TestInstance +from test_framework.comptool import TestManager, TestInstance, RejectResult from test_framework.blocktools import * import time from test_framework.key import CECKey @@ -45,16 +45,10 @@ self.coinbase_pubkey = self.coinbase_key.get_pubkey() self.tip = None self.blocks = {} - - def setup_network(self): - self.extra_args = [['-norelaypriority']] - self.add_nodes(self.num_nodes, self.extra_args) - self.start_nodes() - - def add_options(self, parser): - super().add_options(parser) - parser.add_argument( - "--runbarelyexpensive", dest="runbarelyexpensive", default=True) + self.extra_args = [["-whitelist=127.0.0.1", + "-acceptnonstdtxn", + "-promiscuousmempoolflags=1", + "-norelaypriority"]] def run_test(self): self.test = TestManager(self, self.options.tmpdir) @@ -149,6 +143,14 @@ else: return TestInstance([[self.tip, reject]]) + # returns a test case that asserts that the transaction was accepted + def tx_accepted(tx): + return TestInstance([[tx, True]]) + + # returns a test case that asserts that the transaction was rejected + def tx_rejected(tx, reject): + return TestInstance([[tx, reject]]) + # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] @@ -235,7 +237,8 @@ [OP_CHECKSIG] * (p2sh_sigops_limit_mempool + 1)) # A transaction with this output script can't get into the mempool - assert_raises_rpc_error(-26, RPC_TXNS_TOO_MANY_SIGOPS_ERROR, node.sendrawtransaction, + assert_raises_rpc_error( + -26, RPC_TXNS_TOO_MANY_SIGOPS_ERROR, node.sendrawtransaction, ToHex(spend_p2sh_tx(p2sh_tx, too_many_p2sh_sigops_mempool))) # The transaction is rejected, so the mempool should still be empty @@ -259,6 +262,54 @@ # The transaction has been mined, it's not in the mempool anymore assert_equal(set(node.getrawmempool()), set()) + def create_funding_tx(txin, n, outputs=10): + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(txin.sha256, n))] + amount = txin.vout[0].nValue // outputs + for _ in range(outputs): + tx.vout.append(CTxOut(amount, CScript([OP_TRUE]))) + tx.vout[0].nValue -= node.calculate_fee(tx) + self.sign_tx(tx, txin, n) + tx.rehash() + return tx + + # Mine a funding txn with many outputs with scriptPub=OP_TRUE + ftx = create_funding_tx(out[2].tx, out[2].n) + block(3) + update_block(3, [ftx]) + yield accepted() + + def create_spending_tx(txin, n, scriptSig, scriptPub=CScript([OP_TRUE])): + tx = create_transaction( + txin, n, scriptSig, txin.vout[n].nValue, scriptPub) + tx.vout[0].nValue -= node.calculate_fee(tx) + tx.rehash() + return tx + + # Tests the CLEANSTACK rule + cleanstack_tx = create_spending_tx(ftx, 0, CScript([OP_TRUE])) + yield tx_rejected(cleanstack_tx, RejectResult(16, b'mandatory-script-verify-flag-failed (Script did not clean its stack)')) + + # Tests the SIGPUSHONLY rule + sigpushonly_tx = create_spending_tx( + ftx, 1, CScript([OP_TRUE, OP_DROP])) + yield tx_rejected(sigpushonly_tx, RejectResult(16, b'mandatory-script-verify-flag-failed (Only push operators allowed in signature scripts)')) + + # Tests CHECKDATASIG (inside a P2SH script) + signature = bytearray.fromhex( + '30440220256c12175e809381f97637933ed6ab97737d263eaaebca6add21bced67fd12a402205ce29ecc1369d6fc1b51977ed38faaf41119e3be1d7edfafd7cfaf0b6061bd07') + message = bytearray.fromhex('') + pubkey = bytearray.fromhex( + '038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508') + redeem_script = CScript([pubkey, OP_CHECKDATASIG]) + cds_fundtx = create_spending_tx( + ftx, 2, CScript(), CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL])) + cds_fundtx_id = node.sendrawtransaction(ToHex(cds_fundtx)) + assert(cds_fundtx_id in node.getrawmempool()) + checkdatasig_tx = create_spending_tx( + cds_fundtx, 0, CScript([signature, message, redeem_script])) + yield tx_accepted(checkdatasig_tx) + if __name__ == '__main__': FullBlockTest().main()