You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
monero-python/monero/transaction/extra.py

96 lines
3.0 KiB

import binascii
import varint
class ExtraParser(object):
TX_EXTRA_TAG_PADDING = 0x00
TX_EXTRA_TAG_PUBKEY = 0x01
TX_EXTRA_TAG_EXTRA_NONCE = 0x02
TX_EXTRA_TAG_ADDITIONAL_PUBKEYS = 0x04
KNOWN_TAGS = (
TX_EXTRA_TAG_PADDING,
TX_EXTRA_TAG_PUBKEY,
TX_EXTRA_TAG_EXTRA_NONCE,
TX_EXTRA_TAG_ADDITIONAL_PUBKEYS,
)
def __init__(self, extra):
if isinstance(extra, str):
extra = binascii.unhexlify(extra)
if isinstance(extra, bytes):
extra = list(extra)
self.extra = extra
def parse(self):
self.data = {}
self.offset = 0
self._parse(self.extra)
return self.data
def _strip_padding(self, extra):
while extra and extra[0] == self.TX_EXTRA_TAG_PADDING:
extra = extra[1:]
self.offset += 1
return extra
def _pop_pubkey(self, extra):
key = bytes(bytearray(extra[:32])) # bytearray() is for py2 compatibility
if len(key) < 32:
raise ValueError(
"offset {:d}: only {:d} bytes of key data, expected 32".format(
self.offset, len(key)
)
)
if "pubkeys" in self.data:
self.data["pubkeys"].append(key)
else:
self.data["pubkeys"] = [key]
extra = extra[32:]
self.offset += 32
return extra
def _extract_pubkey(self, extra):
if extra:
if extra[0] == self.TX_EXTRA_TAG_PUBKEY:
extra = extra[1:]
self.offset += 1
extra = self._pop_pubkey(extra)
elif extra[0] == self.TX_EXTRA_TAG_ADDITIONAL_PUBKEYS:
extra = extra[1:]
self.offset += 1
keycount = varint.decode_bytes(bytearray(extra))
valen = len(varint.encode(keycount))
extra = extra[valen:]
self.offset += valen
for i in range(keycount):
extra = self._pop_pubkey(extra)
return extra
def _extract_nonce(self, extra):
if extra and extra[0] == self.TX_EXTRA_TAG_EXTRA_NONCE:
noncelen = extra[1]
extra = extra[2:]
self.offset += 2
if noncelen > len(extra):
raise ValueError(
"offset {:d}: extra nonce exceeds field size".format(self.offset)
)
nonce = bytearray(extra[:noncelen])
if "nonces" in self.data:
self.data["nonces"].append(nonce)
else:
self.data["nonces"] = [nonce]
extra = extra[noncelen:]
self.offset += noncelen
return extra
def _parse(self, extra):
while extra:
if extra[0] not in self.KNOWN_TAGS:
raise ValueError(
"offset {:d}: unknown tag 0x{:x}".format(self.offset, extra[0])
)
extra = self._strip_padding(extra)
extra = self._extract_pubkey(extra)
extra = self._extract_nonce(extra)