Ensure that successive diversified UAs have the same structure.

In 23321662d1, the unified address
test vectors were modified to include multiple addresses for each
account at successive diversifier indices. However, the generation
process resulted in such diversified addresses having varying sets
of typecodes within a single account; in particular, p2sh and p2pkh
addresses were interleaved in a manner that made testing transparent
functionality using the resulting test vectors more difficult.

In the process of this fix, it was also noted that the handling of
unknown typecodes was inconsistent, with the unknown typecode always
being included in the resulting test vector even if no data
corresponding to that typecode was present. This has also been
modified such that if an unknown typecode is present, data is present,
and vice versa.
This commit is contained in:
Kris Nuttycombe 2022-09-28 09:28:35 -06:00
parent ac92513d4e
commit fb5f931e5f
1 changed files with 36 additions and 25 deletions

View File

@ -39,7 +39,11 @@ def main():
test_vectors = []
for account in range(0, 20):
# Each set of sequential diversified addresses should have the same set
# of typecodes, to simplify use in tests.
has_t_addr = rand.bool()
# use p2pkh 3/4 of the time
is_p2pkh = rand.bool() or rand.bool()
if has_t_addr:
# This randomness is only used if this UA will have a P2SH key.
# If it will have a P2PKH key, it gets overwritten below (after
@ -48,53 +52,58 @@ def main():
else:
t_addr = None
has_s_addr = rand.bool()
has_o_addr = (not has_s_addr) or rand.bool()
# include an unknown item 1/4 of the time
has_unknown_item = rand.bool() and rand.bool()
# use the range reserved for experimental typecodes for unknowns
unknown_tc = rng.randrange(0xFFFA, 0xFFFF+1)
unknown_len = rng.randrange(32, 256)
# we will increment the diversifier index after generating each sample
# within the current account
j = 0
for _ in range(0, 3):
has_s_addr = rand.bool()
receivers = []
if has_s_addr:
s_account_key = s_coin_key.child(hardened(account))
j = s_account_key.find_j(j)
sapling_d = s_account_key.diversifier(j)
sapling_pk_d = s_account_key.pk_d(j)
sapling_raw_addr = sapling_d + bytes(sapling_pk_d)
receivers.append((SAPLING_ITEM, sapling_raw_addr))
else:
sapling_raw_addr = None
has_o_addr = (not has_s_addr) or rand.bool()
if has_o_addr:
o_account_key = o_coin_key.child(hardened(account))
orchard_fvk = orchard_key_components.FullViewingKey.from_spending_key(o_account_key)
orchard_d = orchard_fvk.diversifier(j)
orchard_pk_d = orchard_fvk.pk_d(j)
orchard_raw_addr = orchard_d + bytes(orchard_pk_d)
receivers.append((ORCHARD_ITEM, orchard_raw_addr))
else:
orchard_raw_addr = None
is_p2pkh = rand.bool()
if has_t_addr and is_p2pkh:
t_account_key = t_coin_key.child(hardened(account))
t_external_key = t_account_key.child(0)
t_index_key = t_external_key.child(j)
t_index_pubkey = t_index_key.public_key()
t_addr = t_index_pubkey.address()
if has_t_addr:
if is_p2pkh:
t_account_key = t_coin_key.child(hardened(account))
t_external_key = t_account_key.child(0)
t_index_key = t_external_key.child(j)
t_index_pubkey = t_index_key.public_key()
t_addr = t_index_pubkey.address()
receivers.append((P2PKH_ITEM, t_addr))
else:
receivers.append((P2SH_ITEM, t_addr))
else:
t_addr = None
# include an unknown item 1/4 of the time
has_unknown_item = rand.bool() and rand.bool()
# use the range reserved for experimental typecodes for unknowns
unknown_tc = rng.randrange(0xFFFA, 0xFFFF+1)
unknown_len = rng.randrange(32, 256)
if has_unknown_item:
unknown_bytes = b"".join([rand.b(unknown_len)])
receivers.append((unknown_tc, unknown_bytes))
else:
unknown_bytes = None
receivers = [
(ORCHARD_ITEM, orchard_raw_addr),
(SAPLING_ITEM, sapling_raw_addr),
(P2PKH_ITEM, t_addr if is_p2pkh else None),
(P2SH_ITEM, None if is_p2pkh else t_addr),
(unknown_tc, unknown_bytes),
]
ua = encode_unified(rng, receivers, "u")
expected_lengths = {
@ -108,15 +117,17 @@ def main():
assert decoded.get('orchard') == orchard_raw_addr
assert decoded.get('sapling') == sapling_raw_addr
assert decoded.get('transparent') == t_addr
assert decoded.get('unknown') == ((unknown_tc, unknown_bytes) if unknown_bytes else None)
assert decoded.get('transparent') == t_addr
if has_unknown_item:
assert decoded.get('unknown') == ((unknown_tc, unknown_bytes))
else:
assert decoded.get('unknown') == None
test_vectors.append({
'p2pkh_bytes': t_addr if is_p2pkh else None,
'p2sh_bytes': None if is_p2pkh else t_addr,
'sapling_raw_addr': sapling_raw_addr,
'orchard_raw_addr': orchard_raw_addr,
'unknown_typecode': unknown_tc,
'unknown_typecode': unknown_tc if has_unknown_item else None,
'unknown_bytes': unknown_bytes,
'unified_addr': ua,
'root_seed': seed,
@ -134,7 +145,7 @@ def main():
('p2sh_bytes', 'Option<[u8; 20]>'),
('sapling_raw_addr', 'Option<[u8; 43]>'),
('orchard_raw_addr', 'Option<[u8; 43]>'),
('unknown_typecode', 'u32'),
('unknown_typecode', 'Option<u32>'),
('unknown_bytes', {'rust_type': 'Option<Vec<u8>>', 'bitcoin_flavoured': False}),
('unified_addr', {'rust_type': '&\'static str'}),
('root_seed', {'rust_type': 'Vec<u8>', 'bitcoin_flavoured': False}),