From fb5f931e5f0f084364b8e99eb26f5adeab9ddde8 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 28 Sep 2022 09:28:35 -0600 Subject: [PATCH] Ensure that successive diversified UAs have the same structure. In 23321662d1b36003ea178d3a55db7890ffb7a6d0, 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. --- zcash_test_vectors/unified_address.py | 61 ++++++++++++++++----------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/zcash_test_vectors/unified_address.py b/zcash_test_vectors/unified_address.py index d271c19..996cfce 100755 --- a/zcash_test_vectors/unified_address.py +++ b/zcash_test_vectors/unified_address.py @@ -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'), ('unknown_bytes', {'rust_type': 'Option>', 'bitcoin_flavoured': False}), ('unified_addr', {'rust_type': '&\'static str'}), ('root_seed', {'rust_type': 'Vec', 'bitcoin_flavoured': False}),