zcash-test-vectors/zcash_test_vectors/unified_encoding.py

86 lines
2.7 KiB
Python

import sys; assert sys.version_info[0] >= 3, "Python 3 required."
from random import Random
from .zc_utils import write_compact_size, parse_compact_size
from .bech32m import bech32_encode, bech32_decode, convertbits, Encoding
from .f4jumble import f4jumble, f4jumble_inv
P2PKH_ITEM = 0x00
P2SH_ITEM = 0x01
SAPLING_ITEM = 0x02
ORCHARD_ITEM = 0x03
def tlv(typecode, value):
return b"".join([write_compact_size(typecode), write_compact_size(len(value)), value])
def padding(hrp):
assert(len(hrp) <= 16)
return bytes(hrp, "utf8") + bytes(16 - len(hrp))
def encode_unified(items, hrp):
encoded_items = []
has_p2pkh = False
has_p2sh = False
for item in sorted(items):
if item[1]:
if item[0] == P2PKH_ITEM:
has_p2pkh = True
if item[0] == P2SH_ITEM:
has_p2sh = True
assert (not (has_p2pkh and has_p2sh))
encoded_items.append(tlv(item[0], item[1]))
encoded_items.append(padding(hrp))
r_bytes = b"".join(encoded_items)
converted = convertbits(f4jumble(r_bytes), 8, 5)
return bech32_encode(hrp, converted, Encoding.BECH32M)
def decode_unified(encoded, expected_hrp, expected_lengths):
(hrp, data, encoding) = bech32_decode(encoded)
assert hrp == expected_hrp and encoding == Encoding.BECH32M
assert(len(data) >= 48)
decoded = f4jumble_inv(bytes(convertbits(data, 5, 8, False)))
suffix = decoded[-16:]
# check trailing padding bytes
assert suffix == padding(hrp)
rest = decoded[:-16]
result = {}
prev_type = -1
while len(rest) > 0:
(item_type, rest) = parse_compact_size(rest)
(item_len, rest) = parse_compact_size(rest)
expected_len = expected_lengths.get(item_type)
if expected_len is not None:
assert item_len == expected_len, "incorrect item length"
assert len(rest) >= item_len
(item, rest) = (rest[:item_len], rest[item_len:])
if item_type == P2PKH_ITEM or item_type == P2SH_ITEM:
assert not ('transparent' in result), "duplicate transparent item detected"
result['transparent'] = item
elif item_type == SAPLING_ITEM:
assert not ('sapling' in result), "duplicate sapling item detected"
result['sapling'] = item
elif item_type == ORCHARD_ITEM:
assert not ('orchard' in result), "duplicate orchard item detected"
result['orchard'] = item
else:
assert not ('unknown' in result), "duplicate unknown item detected"
result['unknown'] = (item_type, item)
assert item_type > prev_type, "items out of order: typecodes %r and %r" % (prev_type, item_type)
prev_type = item_type
return result