diff --git a/src/core_io.h b/src/core_io.h index 863b0b98c4..bc3ada2ada 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -26,7 +26,15 @@ CScript ParseScript(const std::string& s); std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); [[nodiscard]] bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx); [[nodiscard]] bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); -uint256 ParseHashStr(const std::string&, const std::string& strName); +/** + * Parse a hex string into 256 bits + * @param[in] strHex a hex-formatted, 64-character string + * @param[out] result the result of the parasing + * @returns true if successful, false if not + * + * @see ParseHashV for an RPC-oriented version of this + */ +bool ParseHashStr(const std::string& strHex, uint256& result); std::vector ParseHexUV(const UniValue& v, const std::string& strName); bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error); int ParseSighashString(const UniValue& sighash); diff --git a/src/core_read.cpp b/src/core_read.cpp index a98e9e046a..2c55552815 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -142,14 +142,13 @@ bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, return true; } -uint256 ParseHashStr(const std::string& strHex, const std::string& strName) +bool ParseHashStr(const std::string& strHex, uint256& result) { - if (!IsHex(strHex)) // Note: IsHex("") is false - throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); + if ((strHex.size() != 64) || !IsHex(strHex)) + return false; - uint256 result; result.SetHex(strHex); - return result; + return true; } std::vector ParseHexUV(const UniValue& v, const std::string& strName) diff --git a/src/dash-tx.cpp b/src/dash-tx.cpp index ed30ace65e..f5de915240 100644 --- a/src/dash-tx.cpp +++ b/src/dash-tx.cpp @@ -222,10 +222,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu throw std::runtime_error("TX input missing separator"); // extract and validate TXID - std::string strTxid = vStrInputParts[0]; - if ((strTxid.size() != 64) || !IsHex(strTxid)) + uint256 txid; + if (!ParseHashStr(vStrInputParts[0], txid)) { throw std::runtime_error("invalid TX input txid"); - uint256 txid(uint256S(strTxid)); + } static const unsigned int minTxOutSz = 9; static const unsigned int maxVout = MaxBlockSize() / minTxOutSz; @@ -547,7 +547,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) if (!prevOut.checkObject(types)) throw std::runtime_error("prevtxs internal object typecheck fail"); - uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid"); + uint256 txid; + if (!ParseHashStr(prevOut["txid"].get_str(), txid)) { + throw std::runtime_error("txid must be hexadecimal string (not '" + prevOut["txid"].get_str() + "')"); + } const int nOut = prevOut["vout"].get_int(); if (nOut < 0) diff --git a/src/rest.cpp b/src/rest.cpp index 946ebfde11..c0f46525c4 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -101,15 +101,6 @@ static std::string AvailableDataFormatsString() return formats; } -static bool ParseHashStr(const std::string& strReq, uint256& v) -{ - if (!IsHex(strReq) || (strReq.size() != 64)) - return false; - - v.SetHex(strReq); - return true; -} - static bool CheckWarmup(HTTPRequest* req) { std::string statusmessage; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index bc685f421f..759c370601 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -261,7 +261,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) LOCK(cs_main); - uint256 hash = ParseHashStr(request.params[0].get_str(), "txid"); + uint256 hash = ParseHashV(request.params[0].get_str(), "txid"); CAmount nAmount = request.params[1].get_int64(); mempool.PrioritiseTransaction(hash, nAmount); diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp index 6f93bc76e2..f888593989 100644 --- a/src/test/blockfilter_tests.cpp +++ b/src/test/blockfilter_tests.cpp @@ -114,7 +114,8 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test) unsigned int pos = 0; /*int block_height =*/ test[pos++].get_int(); - /*uint256 block_hash =*/ ParseHashStr(test[pos++].get_str(), "block_hash"); + uint256 block_hash; + BOOST_CHECK(ParseHashStr(test[pos++].get_str(), block_hash)); CBlock block; BOOST_REQUIRE(DecodeHexBlk(block, test[pos++].get_str())); @@ -129,9 +130,11 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test) tx_undo.vprevout.emplace_back(txout, 0, false); } - uint256 prev_filter_header_basic = ParseHashStr(test[pos++].get_str(), "prev_filter_header_basic"); + uint256 prev_filter_header_basic; + BOOST_CHECK(ParseHashStr(test[pos++].get_str(), prev_filter_header_basic)); std::vector filter_basic = ParseHex(test[pos++].get_str()); - uint256 filter_header_basic = ParseHashStr(test[pos++].get_str(), "filter_header_basic"); + uint256 filter_header_basic; + BOOST_CHECK(ParseHashStr(test[pos++].get_str(), filter_header_basic)); BlockFilter computed_filter_basic(BlockFilterType::BASIC_FILTER, block, block_undo); BOOST_CHECK(computed_filter_basic.GetFilter().GetEncoded() == filter_basic); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 337e491937..473a3edf17 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3945,6 +3945,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) // Sign the transaction LOCK2(cs_main, mempool.cs); LOCK(pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); + return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]); } diff --git a/test/functional/README.md b/test/functional/README.md index 6f82314c09..218439ab2c 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -64,6 +64,11 @@ don't have test cases for. - When calling RPCs with lots of arguments, consider using named keyword arguments instead of positional arguments to make the intent of the call clear to readers. +- Many of the core test framework classes such as `CBlock` and `CTransaction` + don't allow new attributes to be added to their objects at runtime like + typical Python objects allow. This helps prevent unpredictable side effects + from typographical errors or usage of the objects outside of their intended + purpose. #### RPC and P2P definitions @@ -76,7 +81,7 @@ P2P messages. These can be found in the following source files: #### Using the P2P interface -- `mininode.py` contains all the definitions for objects that pass +- `messages.py` contains all the definitions for objects that pass over the network (`CBlock`, `CTransaction`, etc, along with the network-level wrappers for them, `msg_block`, `msg_tx`, etc). diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index 28709c084a..79e0066cc2 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -23,7 +23,8 @@ class PrioritiseTransactionTest(BitcoinTestFramework): assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0) # Test `prioritisetransaction` invalid `txid` - assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) + assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) + assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000')", self.nodes[0].prioritisetransaction, txid='Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000', fee_delta=0) txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index d316437678..7f473ff26b 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -46,6 +46,14 @@ class SignRawTransactionsTest(BitcoinTestFramework): rawTxSigned2 = self.nodes[0].signrawtransaction(rawTx, inputs, privKeys) assert_equal(rawTxSigned, rawTxSigned2) + def test_with_lock_outputs(self): + """Test correct error reporting when trying to sign a locked output""" + self.nodes[0].encryptwallet("password") + + rawTx = '020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000' + + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signrawtransactionwithwallet, rawTx) + def script_verification_error_test(self): """Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script. @@ -117,6 +125,7 @@ class SignRawTransactionsTest(BitcoinTestFramework): def run_test(self): self.successful_signing_test() self.script_verification_error_test() + self.test_with_lock_outputs() if __name__ == '__main__': diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index e07a5318c2..1b49309446 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -10,7 +10,12 @@ CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....: bitcoin/primitives msg_block, msg_tx, msg_headers, etc.: data structures that represent network messages -ser_*, deser_*: functions that handle serialization/deserialization.""" + +ser_*, deser_*: functions that handle serialization/deserialization. + +Classes use __slots__ to ensure extraneous attributes aren't accidentally added +by tests, compromising their intended effect. +""" from codecs import encode import copy from collections import namedtuple @@ -215,7 +220,9 @@ def ToHex(obj): # Objects that map to dashd objects, which can be serialized/deserialized -class CService(): +class CService: + __slots__ = ("ip", "port") + def __init__(self): self.ip = "" self.port = 0 @@ -234,7 +241,7 @@ class CService(): return "CService(ip=%s port=%i)" % (self.ip, self.port) -class CAddress(): +class CAddress: __slots__ = ("net", "ip", "nServices", "port", "time") # see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki @@ -313,7 +320,9 @@ class CAddress(): % (self.nServices, self.ADDRV2_NET_NAME[self.net], self.ip, self.port)) -class CInv(): +class CInv: + __slots__ = ("hash", "type") + typemap = { 0: "Error", 1: "TX", @@ -340,7 +349,9 @@ class CInv(): % (self.typemap.get(self.type, "%d" % self.type), self.hash) -class CBlockLocator(): +class CBlockLocator: + __slots__ = ("nVersion", "vHave") + def __init__(self): self.nVersion = MY_VERSION self.vHave = [] @@ -360,7 +371,9 @@ class CBlockLocator(): % (self.nVersion, repr(self.vHave)) -class COutPoint(): +class COutPoint: + __slots__ = ("hash", "n") + def __init__(self, hash=0, n=0xFFFFFFFF): self.hash = hash self.n = n @@ -379,7 +392,9 @@ class COutPoint(): return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n) -class CTxIn(): +class CTxIn: + __slots__ = ("nSequence", "prevout", "scriptSig") + def __init__(self, outpoint=None, scriptSig=b"", nSequence=0): if outpoint is None: self.prevout = COutPoint() @@ -407,7 +422,9 @@ class CTxIn(): self.nSequence) -class CTxOut(): +class CTxOut: + __slots__ = ("nValue", "scriptPubKey") + def __init__(self, nValue=0, scriptPubKey=b""): self.nValue = nValue self.scriptPubKey = scriptPubKey @@ -428,7 +445,10 @@ class CTxOut(): bytes_to_hex_str(self.scriptPubKey)) -class CTransaction(): +class CTransaction: + __slots__ = ("hash", "nLockTime", "nVersion", "sha256", "vin", "vout", + "nType", "vExtraPayload") + def __init__(self, tx=None): if tx is None: self.nVersion = 1 @@ -494,7 +514,10 @@ class CTransaction(): % (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime) -class CBlockHeader(): +class CBlockHeader: + __slots__ = ("hash", "hashMerkleRoot", "hashPrevBlock", "nBits", "nNonce", + "nTime", "nVersion", "sha256") + def __init__(self, header=None): if header is None: self.set_null() @@ -563,6 +586,8 @@ class CBlockHeader(): class CBlock(CBlockHeader): + __slots__ = ("vtx",) + def __init__(self, header=None): super(CBlock, self).__init__(header) self.vtx = [] @@ -619,7 +644,9 @@ class CBlock(CBlockHeader): % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) -class PrefilledTransaction(): +class PrefilledTransaction: + __slots__ = ("index", "tx") + def __init__(self, index=0, tx = None): self.index = index self.tx = tx @@ -638,8 +665,12 @@ class PrefilledTransaction(): def __repr__(self): return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx)) + # This is what we send on the wire, in a cmpctblock message. -class P2PHeaderAndShortIDs(): +class P2PHeaderAndShortIDs: + __slots__ = ("header", "nonce", "prefilled_txn", "prefilled_txn_length", + "shortids", "shortids_length") + def __init__(self): self.header = CBlockHeader() self.nonce = 0 @@ -680,9 +711,12 @@ def calculate_shortid(k0, k1, tx_hash): expected_shortid &= 0x0000ffffffffffff return expected_shortid + # This version gets rid of the array lengths, and reinterprets the differential # encoding into indices that can be used for lookup. -class HeaderAndShortIDs(): +class HeaderAndShortIDs: + __slots__ = ("header", "nonce", "prefilled_txn", "shortids") + def __init__(self, p2pheaders_and_shortids = None): self.header = CBlockHeader() self.nonce = 0 @@ -734,7 +768,8 @@ class HeaderAndShortIDs(): return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn)) -class BlockTransactionsRequest(): +class BlockTransactionsRequest: + __slots__ = ("blockhash", "indexes") def __init__(self, blockhash=0, indexes = None): self.blockhash = blockhash @@ -774,7 +809,8 @@ class BlockTransactionsRequest(): return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes)) -class BlockTransactions(): +class BlockTransactions: + __slots__ = ("blockhash", "transactions") def __init__(self, blockhash=0, transactions = None): self.blockhash = blockhash @@ -794,7 +830,9 @@ class BlockTransactions(): return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) -class CPartialMerkleTree(): +class CPartialMerkleTree: + __slots__ = ("fBad", "nTransactions", "vBits", "vHash") + def __init__(self): self.nTransactions = 0 self.vBits = [] @@ -817,7 +855,9 @@ class CPartialMerkleTree(): return "CPartialMerkleTree(nTransactions=%d vBits.size=%d vHash.size=%d)" % (self.nTransactions, len(self.vBits), len(self.vHash)) -class CMerkleBlock(): +class CMerkleBlock: + __slots__ = ("header", "txn") + def __init__(self, header=CBlockHeader(), txn=CPartialMerkleTree()): self.header = header self.txn = txn @@ -836,7 +876,9 @@ class CMerkleBlock(): return "CMerkleBlock(header=%s txn=%s)" % (repr(self.header), repr(self.txn)) -class CCbTx(): +class CCbTx: + __slots__ = ("version", "height", "merkleRootMNList", "merkleRootQuorums") + def __init__(self, version=None, height=None, merkleRootMNList=None, merkleRootQuorums=None): self.set_null() if version is not None: @@ -870,7 +912,9 @@ class CCbTx(): return r -class CSimplifiedMNListEntry(): +class CSimplifiedMNListEntry: + __slots__ = ("proRegTxHash", "confirmedHash", "service", "pubKeyOperator", "keyIDVoting", "isValid") + def __init__(self): self.set_null() @@ -902,6 +946,9 @@ class CSimplifiedMNListEntry(): class CFinalCommitment: + __slots__ = ("nVersion", "llmqType", "quorumHash", "signers", "validMembers", "quorumPublicKey", + "quorumVvecHash", "quorumSig", "membersSig") + def __init__(self): self.set_null() @@ -942,6 +989,9 @@ class CFinalCommitment: class CGovernanceObject: + __slots__ = ("nHashParent", "nRevision", "nTime", "nCollateralHash", "vchData", "nObjectType", + "masternodeOutpoint", "vchSig") + def __init__(self): self.nHashParent = 0 self.nRevision = 0 @@ -982,6 +1032,8 @@ class CGovernanceObject: class CGovernanceVote: + __slots__ = ("masternodeOutpoint", "nParentHash", "nVoteOutcome", "nVoteSignal", "nTime", "vchSig") + def __init__(self): self.masternodeOutpoint = COutPoint() self.nParentHash = 0 @@ -1013,6 +1065,8 @@ class CGovernanceVote: class CRecoveredSig: + __slots__ = ("llmqType", "quorumHash", "id", "msgHash", "sig") + def __init__(self): self.llmqType = 0 self.quorumHash = 0 @@ -1038,6 +1092,8 @@ class CRecoveredSig: class CSigShare: + __slots__ = ("llmqType", "quorumHash", "quorumMember", "id", "msgHash", "sigShare") + def __init__(self): self.llmqType = 0 self.quorumHash = 0 @@ -1066,6 +1122,8 @@ class CSigShare: class CBLSPublicKey: + __slots__ = ("data") + def __init__(self): self.data = b'\\x0' * 48 @@ -1079,6 +1137,8 @@ class CBLSPublicKey: class CBLSIESEncryptedSecretKey: + __slots__ = ("ephemeral_pubKey", "iv", "data") + def __init__(self): self.ephemeral_pubKey = b'\\x0' * 48 self.iv = b'\\x0' * 32 @@ -1100,7 +1160,9 @@ class CBLSIESEncryptedSecretKey: # Objects that correspond to messages on the wire -class msg_version(): +class msg_version: + __slots__ = ("addrFrom", "addrTo", "nNonce", "nRelay", "nServices", + "nStartingHeight", "nTime", "nVersion", "strSubVer") command = b"version" def __init__(self): @@ -1157,7 +1219,8 @@ class msg_version(): self.strSubVer, self.nStartingHeight, self.nRelay) -class msg_verack(): +class msg_verack: + __slots__ = () command = b"verack" def __init__(self): @@ -1173,7 +1236,8 @@ class msg_verack(): return "msg_verack()" -class msg_addr(): +class msg_addr: + __slots__ = ("addrs",) command = b"addr" def __init__(self): @@ -1189,8 +1253,8 @@ class msg_addr(): return "msg_addr(addrs=%s)" % (repr(self.addrs)) -class msg_addrv2(): - # __slots__ = ("addrs",) +class msg_addrv2: + __slots__ = ("addrs",) # msgtype = b"addrv2" command = b"addrv2" @@ -1207,8 +1271,8 @@ class msg_addrv2(): return "msg_addrv2(addrs=%s)" % (repr(self.addrs)) -class msg_sendaddrv2(): - # __slots__ = () +class msg_sendaddrv2: + __slots__ = () # msgtype = b"sendaddrv2" command = b"sendaddrv2" @@ -1225,7 +1289,8 @@ class msg_sendaddrv2(): return "msg_sendaddrv2()" -class msg_inv(): +class msg_inv: + __slots__ = ("inv",) command = b"inv" def __init__(self, inv=None): @@ -1244,7 +1309,8 @@ class msg_inv(): return "msg_inv(inv=%s)" % (repr(self.inv)) -class msg_getdata(): +class msg_getdata: + __slots__ = ("inv",) command = b"getdata" def __init__(self, inv=None): @@ -1260,7 +1326,8 @@ class msg_getdata(): return "msg_getdata(inv=%s)" % (repr(self.inv)) -class msg_getblocks(): +class msg_getblocks: + __slots__ = ("locator", "hashstop") command = b"getblocks" def __init__(self): @@ -1283,7 +1350,8 @@ class msg_getblocks(): % (repr(self.locator), self.hashstop) -class msg_tx(): +class msg_tx: + __slots__ = ("tx",) command = b"tx" def __init__(self, tx=CTransaction()): @@ -1299,7 +1367,8 @@ class msg_tx(): return "msg_tx(tx=%s)" % (repr(self.tx)) -class msg_block(): +class msg_block: + __slots__ = ("block",) command = b"block" def __init__(self, block=None): @@ -1319,7 +1388,9 @@ class msg_block(): # for cases where a user needs tighter control over what is sent over the wire # note that the user must supply the name of the command, and the data -class msg_generic(): +class msg_generic: + __slots__ = ("command", "data") + def __init__(self, command, data=None): self.command = command self.data = data @@ -1330,7 +1401,9 @@ class msg_generic(): def __repr__(self): return "msg_generic()" -class msg_getaddr(): + +class msg_getaddr: + __slots__ = () command = b"getaddr" def __init__(self): @@ -1346,7 +1419,8 @@ class msg_getaddr(): return "msg_getaddr()" -class msg_ping(): +class msg_ping: + __slots__ = ("nonce",) command = b"ping" def __init__(self, nonce=0): @@ -1364,7 +1438,8 @@ class msg_ping(): return "msg_ping(nonce=%08x)" % self.nonce -class msg_pong(): +class msg_pong: + __slots__ = ("nonce",) command = b"pong" def __init__(self, nonce=0): @@ -1382,7 +1457,8 @@ class msg_pong(): return "msg_pong(nonce=%08x)" % self.nonce -class msg_mempool(): +class msg_mempool: + __slots__ = () command = b"mempool" def __init__(self): @@ -1414,7 +1490,8 @@ class msg_notfound: return "msg_notfound(vec=%s)" % (repr(self.vec)) -class msg_sendheaders(): +class msg_sendheaders: + __slots__ = () command = b"sendheaders" def __init__(self): @@ -1434,7 +1511,8 @@ class msg_sendheaders(): # number of entries # vector of hashes # hash_stop (hash of last desired block header, 0 to get as many as possible) -class msg_getheaders(): +class msg_getheaders: + __slots__ = ("hashstop", "locator",) command = b"getheaders" def __init__(self): @@ -1459,7 +1537,8 @@ class msg_getheaders(): # headers message has # -class msg_headers(): +class msg_headers: + __slots__ = ("headers",) command = b"headers" def __init__(self, headers=None): @@ -1479,7 +1558,8 @@ class msg_headers(): return "msg_headers(headers=%s)" % repr(self.headers) -class msg_reject(): +class msg_reject: + __slots__ = ("code", "data", "message", "reason") command = b"reject" REJECT_MALFORMED = 1 @@ -1511,7 +1591,8 @@ class msg_reject(): % (self.message, self.code, self.reason, self.data) -class msg_sendcmpct(): +class msg_sendcmpct: + __slots__ = ("announce", "version") command = b"sendcmpct" def __init__(self): @@ -1531,7 +1612,9 @@ class msg_sendcmpct(): def __repr__(self): return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version) -class msg_cmpctblock(): + +class msg_cmpctblock: + __slots__ = ("header_and_shortids",) command = b"cmpctblock" def __init__(self, header_and_shortids = None): @@ -1549,7 +1632,9 @@ class msg_cmpctblock(): def __repr__(self): return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids) -class msg_getblocktxn(): + +class msg_getblocktxn: + __slots__ = ("block_txn_request",) command = b"getblocktxn" def __init__(self): @@ -1567,7 +1652,9 @@ class msg_getblocktxn(): def __repr__(self): return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request)) -class msg_blocktxn(): + +class msg_blocktxn: + __slots__ = ("block_transactions",) command = b"blocktxn" def __init__(self): @@ -1584,7 +1671,9 @@ class msg_blocktxn(): def __repr__(self): return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions)) -class msg_getmnlistd(): + +class msg_getmnlistd: + __slots__ = ("baseBlockHash", "blockHash",) command = b"getmnlistd" def __init__(self, baseBlockHash=0, blockHash=0): @@ -1606,7 +1695,8 @@ class msg_getmnlistd(): QuorumId = namedtuple('QuorumId', ['llmqType', 'quorumHash']) -class msg_mnlistdiff(): +class msg_mnlistdiff: + __slots__ = ("baseBlockHash", "blockHash", "merkleProof", "cbTx", "deletedMNs", "mnList", "deletedQuorums", "newQuorums",) command = b"mnlistdiff" def __init__(self): @@ -1648,7 +1738,8 @@ class msg_mnlistdiff(): return "msg_mnlistdiff(baseBlockHash=%064x, blockHash=%064x)" % (self.baseBlockHash, self.blockHash) -class msg_clsig(): +class msg_clsig: + __slots__ = ("height", "blockHash", "sig",) command = b"clsig" def __init__(self, height=0, blockHash=0, sig=b'\\x0' * 96): @@ -1672,7 +1763,8 @@ class msg_clsig(): return "msg_clsig(height=%d, blockHash=%064x)" % (self.height, self.blockHash) -class msg_islock(): +class msg_islock: + __slots__ = ("inputs", "txid", "sig",) command = b"islock" def __init__(self, inputs=[], txid=0, sig=b'\\x0' * 96): @@ -1696,7 +1788,8 @@ class msg_islock(): return "msg_islock(inputs=%s, txid=%064x)" % (repr(self.inputs), self.txid) -class msg_qsigshare(): +class msg_qsigshare: + __slots__ = ("sig_shares",) command = b"qsigshare" def __init__(self, sig_shares=[]): @@ -1714,7 +1807,8 @@ class msg_qsigshare(): return "msg_qsigshare(sigShares=%d)" % (len(self.sig_shares)) -class msg_qwatch(): +class msg_qwatch: + __slots__ = () command = b"qwatch" def __init__(self): @@ -1730,7 +1824,8 @@ class msg_qwatch(): return "msg_qwatch()" -class msg_qgetdata(): +class msg_qgetdata: + __slots__ = ("quorum_hash", "quorum_type", "data_mask", "protx_hash") command = b"qgetdata" def __init__(self, quorum_hash=0, quorum_type=-1, data_mask=0, protx_hash=0): @@ -1761,7 +1856,8 @@ class msg_qgetdata(): self.protx_hash) -class msg_qdata(): +class msg_qdata: + __slots__ = ("quorum_hash", "quorum_type", "data_mask", "protx_hash", "error", "quorum_vvec", "enc_contributions",) command = b"qdata" def __init__(self): diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 4dfb787323..df7c7e397f 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -25,7 +25,7 @@ def hash160(s): _opcode_instances = [] class CScriptOp(int): """A single script opcode""" - __slots__ = [] + __slots__ = () @staticmethod def encode_op_pushdata(d): @@ -362,8 +362,11 @@ class CScriptTruncatedPushDataError(CScriptInvalidError): self.data = data super(CScriptTruncatedPushDataError, self).__init__(msg) + # This is used, eg, for blockchain heights in coinbase scripts (bip34) -class CScriptNum(): +class CScriptNum: + __slots__ = ("value",) + def __init__(self, d=0): self.value = d @@ -394,6 +397,8 @@ class CScript(bytes): iter(script) however does iterate by opcode. """ + __slots__ = () + @classmethod def __coerce_instance(cls, other): # Coerce other into bytes diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json index 86e52e58ca..126b28b189 100644 --- a/test/util/data/bitcoin-util-test.json +++ b/test/util/data/bitcoin-util-test.json @@ -102,6 +102,30 @@ "error_txt": "error: Invalid TX locktime requested", "description": "Tests the check for invalid locktime value" }, + { "exec": "./dash-tx", + "args": + ["-create", + "in=Z897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0"], + "return_code": 1, + "error_txt": "error: invalid TX input txid", + "description": "Tests the check for an invalid txid invalid hex" + }, + { "exec": "./dash-tx", + "args": + ["-create", + "in=5897de6bd6:0"], + "return_code": 1, + "error_txt": "error: invalid TX input txid", + "description": "Tests the check for an invalid txid valid hex but too short" + }, + { "exec": "./dash-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f12:0"], + "return_code": 1, + "error_txt": "error: invalid TX input txid", + "description": "Tests the check for an invalid txid valid hex but too long" + }, { "exec": "./dash-tx", "args": ["-create", @@ -251,6 +275,42 @@ "error_txt": "error: prevtxs internal object typecheck fail", "description": "Tests the check for invalid vout index in prevtxs for sign" }, + { "exec": "./dash-tx", + "args": + ["-create", + "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", + "set=privatekeys:[\"7qYrzJZWqnyCWMYswFcqaRJypGdVceudXPSxmZKsngN7fyo7aAV\"]", + "set=prevtxs:[{\"txid\":\"Zd49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59412\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]", + "sign=ALL", + "outaddr=0.001:XijDvbYpPmznwgpWD3DkdYNfGmRP2KoVSk"], + "return_code": 1, + "error_txt": "error: txid must be hexadecimal string (not 'Zd49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59412')", + "description": "Tests the check for invalid txid due to invalid hex" + }, + { "exec": "./dash-tx", + "args": + ["-create", + "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", + "set=privatekeys:[\"7qYrzJZWqnyCWMYswFcqaRJypGdVceudXPSxmZKsngN7fyo7aAV\"]", + "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc594\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]", + "sign=ALL", + "outaddr=0.001:XijDvbYpPmznwgpWD3DkdYNfGmRP2KoVSk"], + "return_code": 1, + "error_txt": "error: txid must be hexadecimal string (not '4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc594')", + "description": "Tests the check for invalid txid valid hex, but too short" + }, + { "exec": "./dash-tx", + "args": + ["-create", + "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", + "set=privatekeys:[\"7qYrzJZWqnyCWMYswFcqaRJypGdVceudXPSxmZKsngN7fyo7aAV\"]", + "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc5948512\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]", + "sign=ALL", + "outaddr=0.001:XijDvbYpPmznwgpWD3DkdYNfGmRP2KoVSk"], + "return_code": 1, + "error_txt": "error: txid must be hexadecimal string (not '4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc5948512')", + "description": "Tests the check for invalid txid valid hex, but too long" + }, { "exec": "./dash-tx", "args": ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"],