From b794e26bd2cbffeb1c2b3adf40edacacd132cf69 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 6 Sep 2026 10:08:41 +0000 Subject: [PATCH] OpReturnData -> DataOutput --- mmgen/proto/btc/tx/base.py | 4 +-- .../tx/{op_return_data.py => data_output.py} | 34 ++++++++++++------- mmgen/proto/btc/tx/new.py | 4 +-- mmgen/tx/file.py | 2 +- test/modtest_d/tx.py | 12 +++---- 5 files changed, 32 insertions(+), 24 deletions(-) rename mmgen/proto/btc/tx/{op_return_data.py => data_output.py} (61%) diff --git a/mmgen/proto/btc/tx/base.py b/mmgen/proto/btc/tx/base.py index a346c951..b496c83f 100755 --- a/mmgen/proto/btc/tx/base.py +++ b/mmgen/proto/btc/tx/base.py @@ -18,7 +18,7 @@ from ....tx.base import Base as TxBase from ....obj import MMGenList, HexStr, ListItemAttr from ....util import msg, make_chksum_6, die, pp_fmt -from .op_return_data import OpReturnData +from .data_output import DataOutput def data2scriptPubKey(data): return '6a' + '{:02x}'.format(len(data)) + data.hex() # OP_RETURN data @@ -188,7 +188,7 @@ class Base(TxBase): _deserialized = None class Output(TxBase.Output): # output contains either addr or data, but not both - data = ListItemAttr(OpReturnData, include_proto=True) # type None in parent cls + data = ListItemAttr(DataOutput, include_proto=True) # type None in parent cls class InputList(TxBase.InputList): diff --git a/mmgen/proto/btc/tx/op_return_data.py b/mmgen/proto/btc/tx/data_output.py similarity index 61% rename from mmgen/proto/btc/tx/op_return_data.py rename to mmgen/proto/btc/tx/data_output.py index b2146831..a4261d0c 100755 --- a/mmgen/proto/btc/tx/op_return_data.py +++ b/mmgen/proto/btc/tx/data_output.py @@ -9,38 +9,46 @@ # https://gitlab.com/mmgen/mmgen-wallet """ -proto.btc.tx.op_return_data: Bitcoin OP_RETURN data class +proto.btc.tx.data_output: Bitcoin data output class """ from ....obj import InitErrors -class OpReturnData(bytes, InitErrors): +class DataOutput(bytes, InitErrors): + + desc = 'OP_RETURN data' + + @property + def max_len(self): + return self.proto.max_op_return_data_len def __new__(cls, proto, data_spec): - desc = 'OP_RETURN data' - - assert isinstance(data_spec, str), f'{desc} argument must be a string' + assert isinstance(data_spec, str), f'{cls.desc} argument must be a string' if data_spec.startswith('hexdata:'): hexdata = data_spec[8:] from ....util import is_hex_str - assert is_hex_str(hexdata), f'{hexdata!r}: {desc} hexdata not in hexadecimal format' - assert not len(hexdata) % 2, f'{len(hexdata)}: {desc} hexdata of non-even length' + assert is_hex_str(hexdata), f'{hexdata!r}: {cls.desc} hexdata not in hexadecimal format' + assert not len(hexdata) % 2, f'{len(hexdata)}: {cls.desc} hexdata of non-even length' ret = bytes.fromhex(hexdata) elif data_spec.startswith('data:'): try: ret = data_spec[5:].encode('utf8') except: - raise ValueError(f'{desc} value must be UTF-8 encoded') + raise ValueError(f'{cls.desc} value must be UTF-8 encoded') else: - raise ValueError(f'{desc} argument must start with ‘data:’ or ‘hexdata:’') - - assert 1 <= len(ret) <= proto.max_op_return_data_len, ( - f'{len(ret)}: invalid {desc} length: not in range 1-{proto.max_op_return_data_len}') + raise ValueError(f'{cls.desc} argument must start with ‘data:’ or ‘hexdata:’') return bytes.__new__(cls, ret) + def __init__(self, proto, data_spec): + + self.proto = proto + + assert 1 <= len(self) <= self.max_len, ( + f'{len(self)}: invalid {self.desc} length: not in range 1-{self.max_len}') + def __repr__(self): 'return an initialization string' ret = str(self) @@ -66,6 +74,6 @@ class OpReturnData(bytes, InitErrors): from ....color import blue, pink ret = str(self) if add_label: - return blue('OP_RETURN data' + (' (hex): ' if self.display_hex else ': ')) + pink(ret) + return blue(self.desc + (' (hex): ' if self.display_hex else ': ')) + pink(ret) else: return pink(ret) diff --git a/mmgen/proto/btc/tx/new.py b/mmgen/proto/btc/tx/new.py index 24d9fea6..a7272ca8 100755 --- a/mmgen/proto/btc/tx/new.py +++ b/mmgen/proto/btc/tx/new.py @@ -32,8 +32,8 @@ class New(Base, TxNew): if hasattr(self, '_have_op_return_data'): die(1, 'Transaction may have at most one OP_RETURN data output!') self._have_op_return_data = True - from .op_return_data import OpReturnData - OpReturnData(self.proto, arg) # test data for validity + from .data_output import DataOutput + DataOutput(self.proto, arg) # test data for validity return arg @property diff --git a/mmgen/tx/file.py b/mmgen/tx/file.py index 126cd0cf..5f1765cf 100755 --- a/mmgen/tx/file.py +++ b/mmgen/tx/file.py @@ -34,7 +34,7 @@ class txdata_json_encoder(json.JSONEncoder): def default(self, o): if type(o).__name__.endswith('Amt'): return str(o) - elif type(o).__name__ == 'OpReturnData': + elif type(o).__name__.endswith('DataOutput'): return repr(o) else: return json.JSONEncoder.default(self, o) diff --git a/test/modtest_d/tx.py b/test/modtest_d/tx.py index 0ee2e300..ab70004b 100755 --- a/test/modtest_d/tx.py +++ b/test/modtest_d/tx.py @@ -112,9 +112,9 @@ class unit_tests: ut.process_bad_data(bad_data) return True - def op_return_data(self, name, ut, desc='OpReturnData class'): + def data_output(self, name, ut, desc='DataOutput class'): max_len = cfg._proto.max_op_return_data_len - from mmgen.proto.btc.tx.op_return_data import OpReturnData + from mmgen.proto.btc.tx.data_output import DataOutput vecs = [ 'data:=:ETH.ETH:0x86d526d6624AbC0178cF7296cD538Ecc080A95F1:0/1/0', 'hexdata:3d3a4554482e4554483a30783836643532366436363234416243303137' @@ -128,11 +128,11 @@ class unit_tests: 'data:' + gr_uc[:24], ] - assert OpReturnData(cfg._proto, vecs[0]) == OpReturnData(cfg._proto, vecs[1]) + assert DataOutput(cfg._proto, vecs[0]) == DataOutput(cfg._proto, vecs[1]) for vec in vecs: - d = OpReturnData(cfg._proto, vec) - assert d == OpReturnData(cfg._proto, repr(d)) # repr() must return a valid initializer + d = DataOutput(cfg._proto, vec) + assert d == DataOutput(cfg._proto, repr(d)) # repr() must return a valid initializer assert isinstance(d, bytes) assert isinstance(str(d), str) vmsg('-' * 80) @@ -156,7 +156,7 @@ class unit_tests: ] def bad(n): - return lambda: OpReturnData(cfg._proto, bad_data[n]) + return lambda: DataOutput(cfg._proto, bad_data[n]) vmsg('-' * 80) vmsg('Testing error handling:')