Merge #14305: Tests: enforce critical class instance attributes in functional tests, fix segwit test specificity

e460232876 Document fixed attribute behavior in critical test framework classes. (Justin Turner Arthur)
17b42f4122 Check for specific tx acceptance failures based on script signature (Justin Turner Arthur)
3a4449e9ad Strictly enforce instance attrs in critical functional test classes. (Justin Turner Arthur)
1d0ce94a54 Fix for incorrect version attr set on functional test segwit block. (Justin Turner Arthur)
ba923e32a0 test: Fix broken segwit test (practicalswift)

Pull request description:

  No extra attributes will be able to be added to instances of the C++ class ports or of other critical classes without causing an exception. Helps prevent adding or depending on attributes that aren't in the intended object structure. It may prevent issues such as the one fixed in bitcoin/bitcoin#14300.

  This request fixes the erroneous version attribute used in the p2p_segwit.py functional tests. This pull includes the commit from bitcoin/bitcoin#14300.

Tree-SHA512: 1b8c58e7aa0f71075ed5ff3e5be0a5182599108d8cd9bce682feac3b1842508124288e9335432b16a43f40f159c9710899e6d84af1b5868f48c947bc6f3e07ec
This commit is contained in:
MarcoFalke 2018-09-27 11:06:40 -04:00
commit edcf29c9da
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25
4 changed files with 153 additions and 50 deletions

View file

@ -60,6 +60,11 @@ don't have test cases for.
- When calling RPCs with lots of arguments, consider using named keyword - When calling RPCs with lots of arguments, consider using named keyword
arguments instead of positional arguments to make the intent of the call arguments instead of positional arguments to make the intent of the call
clear to readers. 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 #### RPC and P2P definitions
@ -72,7 +77,7 @@ P2P messages. These can be found in the following source files:
#### Using the P2P interface #### 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 over the network (`CBlock`, `CTransaction`, etc, along with the network-level
wrappers for them, `msg_block`, `msg_tx`, etc). wrappers for them, `msg_block`, `msg_tx`, etc).

View file

@ -205,7 +205,7 @@ class SegWitTest(BitcoinTestFramework):
height = self.nodes[0].getblockcount() + 1 height = self.nodes[0].getblockcount() + 1
block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1 block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1
block = create_block(int(tip, 16), create_coinbase(height), block_time) block = create_block(int(tip, 16), create_coinbase(height), block_time)
block.version = version block.nVersion = version
block.rehash() block.rehash()
return block return block
@ -769,12 +769,16 @@ class SegWitTest(BitcoinTestFramework):
# will require a witness to spend a witness program regardless of # will require a witness to spend a witness program regardless of
# segwit activation. Note that older bitcoind's that are not # segwit activation. Note that older bitcoind's that are not
# segwit-aware would also reject this for failing CLEANSTACK. # segwit-aware would also reject this for failing CLEANSTACK.
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) with self.nodes[0].assert_debug_log(
expected_msgs=(spend_tx.hash, 'was not accepted: non-mandatory-script-verify-flag (Witness program was passed an empty witness)')):
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
# Try to put the witness script in the script_sig, should also fail. # Try to put the witness script in the scriptSig, should also fail.
spend_tx.vin[0].script_sig = CScript([p2wsh_pubkey, b'a']) spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a'])
spend_tx.rehash() spend_tx.rehash()
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) with self.nodes[0].assert_debug_log(
expected_msgs=('Not relaying invalid transaction {}'.format(spend_tx.hash), 'was not accepted: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)')):
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
# Now put the witness script in the witness, should succeed after # Now put the witness script in the witness, should succeed after
# segwit activates. # segwit activates.

View file

@ -13,7 +13,11 @@ CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....:
msg_block, msg_tx, msg_headers, etc.: msg_block, msg_tx, msg_headers, etc.:
data structures that represent network messages 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 from codecs import encode
import copy import copy
import hashlib import hashlib
@ -185,7 +189,10 @@ def ToHex(obj):
# Objects that map to bitcoind objects, which can be serialized/deserialized # Objects that map to bitcoind objects, which can be serialized/deserialized
class CAddress():
class CAddress:
__slots__ = ("ip", "nServices", "pchReserved", "port", "time")
def __init__(self): def __init__(self):
self.time = 0 self.time = 0
self.nServices = 1 self.nServices = 1
@ -215,7 +222,10 @@ class CAddress():
return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
self.ip, self.port) self.ip, self.port)
class CInv():
class CInv:
__slots__ = ("hash", "type")
typemap = { typemap = {
0: "Error", 0: "Error",
1: "TX", 1: "TX",
@ -244,7 +254,9 @@ class CInv():
% (self.typemap[self.type], self.hash) % (self.typemap[self.type], self.hash)
class CBlockLocator(): class CBlockLocator:
__slots__ = ("nVersion", "vHave")
def __init__(self): def __init__(self):
self.nVersion = MY_VERSION self.nVersion = MY_VERSION
self.vHave = [] self.vHave = []
@ -264,7 +276,9 @@ class CBlockLocator():
% (self.nVersion, repr(self.vHave)) % (self.nVersion, repr(self.vHave))
class COutPoint(): class COutPoint:
__slots__ = ("hash", "n")
def __init__(self, hash=0, n=0): def __init__(self, hash=0, n=0):
self.hash = hash self.hash = hash
self.n = n self.n = n
@ -283,7 +297,9 @@ class COutPoint():
return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n) 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): def __init__(self, outpoint=None, scriptSig=b"", nSequence=0):
if outpoint is None: if outpoint is None:
self.prevout = COutPoint() self.prevout = COutPoint()
@ -311,7 +327,9 @@ class CTxIn():
self.nSequence) self.nSequence)
class CTxOut(): class CTxOut:
__slots__ = ("nValue", "scriptPubKey")
def __init__(self, nValue=0, scriptPubKey=b""): def __init__(self, nValue=0, scriptPubKey=b""):
self.nValue = nValue self.nValue = nValue
self.scriptPubKey = scriptPubKey self.scriptPubKey = scriptPubKey
@ -332,7 +350,9 @@ class CTxOut():
bytes_to_hex_str(self.scriptPubKey)) bytes_to_hex_str(self.scriptPubKey))
class CScriptWitness(): class CScriptWitness:
__slots__ = ("stack",)
def __init__(self): def __init__(self):
# stack is a vector of strings # stack is a vector of strings
self.stack = [] self.stack = []
@ -347,7 +367,9 @@ class CScriptWitness():
return True return True
class CTxInWitness(): class CTxInWitness:
__slots__ = ("scriptWitness",)
def __init__(self): def __init__(self):
self.scriptWitness = CScriptWitness() self.scriptWitness = CScriptWitness()
@ -364,7 +386,9 @@ class CTxInWitness():
return self.scriptWitness.is_null() return self.scriptWitness.is_null()
class CTxWitness(): class CTxWitness:
__slots__ = ("vtxinwit",)
def __init__(self): def __init__(self):
self.vtxinwit = [] self.vtxinwit = []
@ -392,7 +416,10 @@ class CTxWitness():
return True return True
class CTransaction(): class CTransaction:
__slots__ = ("hash", "nLockTime", "nVersion", "sha256", "vin", "vout",
"wit")
def __init__(self, tx=None): def __init__(self, tx=None):
if tx is None: if tx is None:
self.nVersion = 1 self.nVersion = 1
@ -496,7 +523,10 @@ class CTransaction():
% (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime) % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
class CBlockHeader(): class CBlockHeader:
__slots__ = ("hash", "hashMerkleRoot", "hashPrevBlock", "nBits", "nNonce",
"nTime", "nVersion", "sha256")
def __init__(self, header=None): def __init__(self, header=None):
if header is None: if header is None:
self.set_null() self.set_null()
@ -565,6 +595,8 @@ class CBlockHeader():
class CBlock(CBlockHeader): class CBlock(CBlockHeader):
__slots__ = ("vtx",)
def __init__(self, header=None): def __init__(self, header=None):
super(CBlock, self).__init__(header) super(CBlock, self).__init__(header)
self.vtx = [] self.vtx = []
@ -636,7 +668,9 @@ class CBlock(CBlockHeader):
time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) 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): def __init__(self, index=0, tx = None):
self.index = index self.index = index
self.tx = tx self.tx = tx
@ -664,8 +698,12 @@ class PrefilledTransaction():
def __repr__(self): def __repr__(self):
return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx)) return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx))
# This is what we send on the wire, in a cmpctblock message. # 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): def __init__(self):
self.header = CBlockHeader() self.header = CBlockHeader()
self.nonce = 0 self.nonce = 0
@ -703,9 +741,11 @@ class P2PHeaderAndShortIDs():
def __repr__(self): def __repr__(self):
return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn)) return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn))
# P2P version of the above that will use witness serialization (for compact # P2P version of the above that will use witness serialization (for compact
# block version 2) # block version 2)
class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs): class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs):
__slots__ = ()
def serialize(self): def serialize(self):
return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True) return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True)
@ -715,9 +755,12 @@ def calculate_shortid(k0, k1, tx_hash):
expected_shortid &= 0x0000ffffffffffff expected_shortid &= 0x0000ffffffffffff
return expected_shortid return expected_shortid
# This version gets rid of the array lengths, and reinterprets the differential # This version gets rid of the array lengths, and reinterprets the differential
# encoding into indices that can be used for lookup. # encoding into indices that can be used for lookup.
class HeaderAndShortIDs(): class HeaderAndShortIDs:
__slots__ = ("header", "nonce", "prefilled_txn", "shortids", "use_witness")
def __init__(self, p2pheaders_and_shortids = None): def __init__(self, p2pheaders_and_shortids = None):
self.header = CBlockHeader() self.header = CBlockHeader()
self.nonce = 0 self.nonce = 0
@ -778,7 +821,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)) 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): def __init__(self, blockhash=0, indexes = None):
self.blockhash = blockhash self.blockhash = blockhash
@ -818,7 +862,8 @@ class BlockTransactionsRequest():
return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes)) 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): def __init__(self, blockhash=0, transactions = None):
self.blockhash = blockhash self.blockhash = blockhash
@ -840,7 +885,10 @@ class BlockTransactions():
def __repr__(self): def __repr__(self):
return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
class CPartialMerkleTree():
class CPartialMerkleTree:
__slots__ = ("fBad", "nTransactions", "vBits", "vHash")
def __init__(self): def __init__(self):
self.nTransactions = 0 self.nTransactions = 0
self.vHash = [] self.vHash = []
@ -868,7 +916,10 @@ class CPartialMerkleTree():
def __repr__(self): def __repr__(self):
return "CPartialMerkleTree(nTransactions=%d, vHash=%s, vBits=%s)" % (self.nTransactions, repr(self.vHash), repr(self.vBits)) return "CPartialMerkleTree(nTransactions=%d, vHash=%s, vBits=%s)" % (self.nTransactions, repr(self.vHash), repr(self.vBits))
class CMerkleBlock():
class CMerkleBlock:
__slots__ = ("header", "txn")
def __init__(self): def __init__(self):
self.header = CBlockHeader() self.header = CBlockHeader()
self.txn = CPartialMerkleTree() self.txn = CPartialMerkleTree()
@ -888,7 +939,9 @@ class CMerkleBlock():
# Objects that correspond to messages on the wire # 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" command = b"version"
def __init__(self): def __init__(self):
@ -945,7 +998,8 @@ class msg_version():
self.strSubVer, self.nStartingHeight, self.nRelay) self.strSubVer, self.nStartingHeight, self.nRelay)
class msg_verack(): class msg_verack:
__slots__ = ()
command = b"verack" command = b"verack"
def __init__(self): def __init__(self):
@ -961,7 +1015,8 @@ class msg_verack():
return "msg_verack()" return "msg_verack()"
class msg_addr(): class msg_addr:
__slots__ = ("addrs",)
command = b"addr" command = b"addr"
def __init__(self): def __init__(self):
@ -977,7 +1032,8 @@ class msg_addr():
return "msg_addr(addrs=%s)" % (repr(self.addrs)) return "msg_addr(addrs=%s)" % (repr(self.addrs))
class msg_inv(): class msg_inv:
__slots__ = ("inv",)
command = b"inv" command = b"inv"
def __init__(self, inv=None): def __init__(self, inv=None):
@ -996,7 +1052,8 @@ class msg_inv():
return "msg_inv(inv=%s)" % (repr(self.inv)) return "msg_inv(inv=%s)" % (repr(self.inv))
class msg_getdata(): class msg_getdata:
__slots__ = ("inv",)
command = b"getdata" command = b"getdata"
def __init__(self, inv=None): def __init__(self, inv=None):
@ -1012,7 +1069,8 @@ class msg_getdata():
return "msg_getdata(inv=%s)" % (repr(self.inv)) return "msg_getdata(inv=%s)" % (repr(self.inv))
class msg_getblocks(): class msg_getblocks:
__slots__ = ("locator", "hashstop")
command = b"getblocks" command = b"getblocks"
def __init__(self): def __init__(self):
@ -1035,7 +1093,8 @@ class msg_getblocks():
% (repr(self.locator), self.hashstop) % (repr(self.locator), self.hashstop)
class msg_tx(): class msg_tx:
__slots__ = ("tx",)
command = b"tx" command = b"tx"
def __init__(self, tx=CTransaction()): def __init__(self, tx=CTransaction()):
@ -1050,13 +1109,16 @@ class msg_tx():
def __repr__(self): def __repr__(self):
return "msg_tx(tx=%s)" % (repr(self.tx)) return "msg_tx(tx=%s)" % (repr(self.tx))
class msg_witness_tx(msg_tx): class msg_witness_tx(msg_tx):
__slots__ = ()
def serialize(self): def serialize(self):
return self.tx.serialize_with_witness() return self.tx.serialize_with_witness()
class msg_block(): class msg_block:
__slots__ = ("block",)
command = b"block" command = b"block"
def __init__(self, block=None): def __init__(self, block=None):
@ -1074,9 +1136,12 @@ class msg_block():
def __repr__(self): def __repr__(self):
return "msg_block(block=%s)" % (repr(self.block)) return "msg_block(block=%s)" % (repr(self.block))
# for cases where a user needs tighter control over what is sent over the wire # 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 # 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): def __init__(self, command, data=None):
self.command = command self.command = command
self.data = data self.data = data
@ -1087,13 +1152,16 @@ class msg_generic():
def __repr__(self): def __repr__(self):
return "msg_generic()" return "msg_generic()"
class msg_witness_block(msg_block):
class msg_witness_block(msg_block):
__slots__ = ()
def serialize(self): def serialize(self):
r = self.block.serialize(with_witness=True) r = self.block.serialize(with_witness=True)
return r return r
class msg_getaddr():
class msg_getaddr:
__slots__ = ()
command = b"getaddr" command = b"getaddr"
def __init__(self): def __init__(self):
@ -1109,7 +1177,8 @@ class msg_getaddr():
return "msg_getaddr()" return "msg_getaddr()"
class msg_ping(): class msg_ping:
__slots__ = ("nonce",)
command = b"ping" command = b"ping"
def __init__(self, nonce=0): def __init__(self, nonce=0):
@ -1127,7 +1196,8 @@ class msg_ping():
return "msg_ping(nonce=%08x)" % self.nonce return "msg_ping(nonce=%08x)" % self.nonce
class msg_pong(): class msg_pong:
__slots__ = ("nonce",)
command = b"pong" command = b"pong"
def __init__(self, nonce=0): def __init__(self, nonce=0):
@ -1145,7 +1215,8 @@ class msg_pong():
return "msg_pong(nonce=%08x)" % self.nonce return "msg_pong(nonce=%08x)" % self.nonce
class msg_mempool(): class msg_mempool:
__slots__ = ()
command = b"mempool" command = b"mempool"
def __init__(self): def __init__(self):
@ -1160,7 +1231,9 @@ class msg_mempool():
def __repr__(self): def __repr__(self):
return "msg_mempool()" return "msg_mempool()"
class msg_sendheaders():
class msg_sendheaders:
__slots__ = ()
command = b"sendheaders" command = b"sendheaders"
def __init__(self): def __init__(self):
@ -1180,7 +1253,8 @@ class msg_sendheaders():
# number of entries # number of entries
# vector of hashes # vector of hashes
# hash_stop (hash of last desired block header, 0 to get as many as possible) # 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" command = b"getheaders"
def __init__(self): def __init__(self):
@ -1205,7 +1279,8 @@ class msg_getheaders():
# headers message has # headers message has
# <count> <vector of block headers> # <count> <vector of block headers>
class msg_headers(): class msg_headers:
__slots__ = ("headers",)
command = b"headers" command = b"headers"
def __init__(self, headers=None): def __init__(self, headers=None):
@ -1225,7 +1300,8 @@ class msg_headers():
return "msg_headers(headers=%s)" % repr(self.headers) return "msg_headers(headers=%s)" % repr(self.headers)
class msg_reject(): class msg_reject:
__slots__ = ("code", "data", "message", "reason")
command = b"reject" command = b"reject"
REJECT_MALFORMED = 1 REJECT_MALFORMED = 1
@ -1256,7 +1332,9 @@ class msg_reject():
return "msg_reject: %s %d %s [%064x]" \ return "msg_reject: %s %d %s [%064x]" \
% (self.message, self.code, self.reason, self.data) % (self.message, self.code, self.reason, self.data)
class msg_feefilter():
class msg_feefilter:
__slots__ = ("feerate",)
command = b"feefilter" command = b"feefilter"
def __init__(self, feerate=0): def __init__(self, feerate=0):
@ -1273,7 +1351,9 @@ class msg_feefilter():
def __repr__(self): def __repr__(self):
return "msg_feefilter(feerate=%08x)" % self.feerate return "msg_feefilter(feerate=%08x)" % self.feerate
class msg_sendcmpct():
class msg_sendcmpct:
__slots__ = ("announce", "version")
command = b"sendcmpct" command = b"sendcmpct"
def __init__(self): def __init__(self):
@ -1293,7 +1373,9 @@ class msg_sendcmpct():
def __repr__(self): def __repr__(self):
return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version) return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
class msg_cmpctblock():
class msg_cmpctblock:
__slots__ = ("header_and_shortids",)
command = b"cmpctblock" command = b"cmpctblock"
def __init__(self, header_and_shortids = None): def __init__(self, header_and_shortids = None):
@ -1311,7 +1393,9 @@ class msg_cmpctblock():
def __repr__(self): def __repr__(self):
return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids) return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
class msg_getblocktxn():
class msg_getblocktxn:
__slots__ = ("block_txn_request",)
command = b"getblocktxn" command = b"getblocktxn"
def __init__(self): def __init__(self):
@ -1329,7 +1413,9 @@ class msg_getblocktxn():
def __repr__(self): def __repr__(self):
return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request)) return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
class msg_blocktxn():
class msg_blocktxn:
__slots__ = ("block_transactions",)
command = b"blocktxn" command = b"blocktxn"
def __init__(self): def __init__(self):
@ -1346,7 +1432,10 @@ class msg_blocktxn():
def __repr__(self): def __repr__(self):
return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions)) return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
class msg_witness_blocktxn(msg_blocktxn): class msg_witness_blocktxn(msg_blocktxn):
__slots__ = ()
def serialize(self): def serialize(self):
r = b"" r = b""
r += self.block_transactions.serialize(with_witness=True) r += self.block_transactions.serialize(with_witness=True)

View file

@ -26,7 +26,7 @@ def hash160(s):
_opcode_instances = [] _opcode_instances = []
class CScriptOp(int): class CScriptOp(int):
"""A single script opcode""" """A single script opcode"""
__slots__ = [] __slots__ = ()
@staticmethod @staticmethod
def encode_op_pushdata(d): def encode_op_pushdata(d):
@ -361,8 +361,11 @@ class CScriptTruncatedPushDataError(CScriptInvalidError):
self.data = data self.data = data
super(CScriptTruncatedPushDataError, self).__init__(msg) super(CScriptTruncatedPushDataError, self).__init__(msg)
# This is used, eg, for blockchain heights in coinbase scripts (bip34) # This is used, eg, for blockchain heights in coinbase scripts (bip34)
class CScriptNum(): class CScriptNum:
__slots__ = ("value",)
def __init__(self, d=0): def __init__(self, d=0):
self.value = d self.value = d
@ -393,6 +396,8 @@ class CScript(bytes):
iter(script) however does iterate by opcode. iter(script) however does iterate by opcode.
""" """
__slots__ = ()
@classmethod @classmethod
def __coerce_instance(cls, other): def __coerce_instance(cls, other):
# Coerce other into bytes # Coerce other into bytes