OpReturnData -> DataOutput

This commit is contained in:
The MMGen Project 2026-09-06 10:08:41 +00:00
commit b794e26bd2
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 32 additions and 24 deletions

View file

@ -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):

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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:')