Skip to content

Commit

Permalink
Merge pull request #89 from nuttycom/diversified_ua_sequences
Browse files Browse the repository at this point in the history
Provide UA test vectors for the first 3 valid diversifier indices for each account
  • Loading branch information
nuttycom committed Sep 23, 2022
2 parents 0cbb820 + 73ced0f commit d98b63d
Show file tree
Hide file tree
Showing 8 changed files with 1,083 additions and 397 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test_vectors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Install gnome-keyring
run: sudo apt-get install gnome-keyring

- name: Install poetry
run: pip install --user poetry

Expand Down
124 changes: 11 additions & 113 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ classifiers = [
]

[tool.poetry.dependencies]
python = "^3.7"
numpy = "1.21.0"
python = "^3.8"
numpy = "1.23.3"
chacha20poly1305 = "0.0.3"
cryptography = "36.0.0"
cryptography = "38.0.1"
secp256k1 = "0.14.0"
base58 = "2.1.1"

Expand Down
80 changes: 60 additions & 20 deletions test-vectors/json/unified_address.json

Large diffs are not rendered by default.

1,024 changes: 857 additions & 167 deletions test-vectors/rust/unified_address.rs

Large diffs are not rendered by default.

80 changes: 60 additions & 20 deletions test-vectors/zcash/unified_address.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions zcash_test_vectors/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ def bitcoinify(value):
if type(value) == list:
return [bitcoinify(v) for v in value]

if type(value) == str:
return value

if type(value) == bytes:
if bitcoin_flavoured and len(value) == 32:
value = value[::-1]
Expand Down Expand Up @@ -92,6 +95,13 @@ def tv_vec_bool_rust(name, value, pad):
pad,
))

def tv_str_rust(name, value, pad):
print('''%s%s: "%s",''' % (
pad,
name,
value,
))

def tv_option_bytes_rust(name, value, pad):
if value:
print('''%s%s: Some([
Expand Down Expand Up @@ -142,6 +152,8 @@ def tv_part_rust(name, value, config, indent=3):
tv_vec_bytes_rust(name, value, pad)
elif config['rust_type'] == 'Vec<bool>':
tv_vec_bool_rust(name, value, pad)
elif config['rust_type'] == '&\'static str':
tv_str_rust(name, value, pad)
elif config['rust_type'].startswith('Option<['):
tv_option_bytes_rust(name, value, pad)
elif type(value) == bytes:
Expand Down
151 changes: 77 additions & 74 deletions zcash_test_vectors/unified_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,79 +49,82 @@ def main():
t_addr = None

j = 0
has_s_addr = rand.bool()
if has_s_addr:
s_account_key = s_coin_key.child(hardened(account))
j = s_account_key.find_j(0)
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)
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)
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_account_key.child(j)
t_index_pubkey = t_index_key.public_key()
t_addr = t_index_pubkey.address()

# 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)])
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 = {
ORCHARD_ITEM: 43,
SAPLING_ITEM: 43,
P2PKH_ITEM: 20,
P2SH_ITEM: 20,
unknown_tc: unknown_len
}
decoded = decode_unified(ua, "u", expected_lengths)
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

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_bytes': unknown_bytes,
'unified_addr': ua.encode(),
'root_seed': seed,
'account': account,
'diversifier_index': j,
})
for _ in range(0, 3):
has_s_addr = rand.bool()
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)
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)
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_account_key.child(j)
t_index_pubkey = t_index_key.public_key()
t_addr = t_index_pubkey.address()

# 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)])
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 = {
ORCHARD_ITEM: 43,
SAPLING_ITEM: 43,
P2PKH_ITEM: 20,
P2SH_ITEM: 20,
unknown_tc: unknown_len
}
decoded = decode_unified(ua, "u", expected_lengths)
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

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_bytes': unknown_bytes,
'unified_addr': ua,
'root_seed': seed,
'account': account,
'diversifier_index': j,
})

j += 1

render_tv(
args,
Expand All @@ -133,7 +136,7 @@ def main():
('orchard_raw_addr', 'Option<[u8; 43]>'),
('unknown_typecode', 'u32'),
('unknown_bytes', {'rust_type': 'Option<Vec<u8>>', 'bitcoin_flavoured': False}),
('unified_addr', {'rust_type': 'Vec<u8>', 'bitcoin_flavoured': False}),
('unified_addr', {'rust_type': '&\'static str'}),
('root_seed', {'rust_type': 'Vec<u8>', 'bitcoin_flavoured': False}),
('account', 'u32'),
('diversifier_index', 'u32'),
Expand Down

0 comments on commit d98b63d

Please sign in to comment.