zcash-test-vectors/zcash_test_vectors/transparent/bip_0032.py

124 lines
3.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
import hashlib
import hmac
from secp256k1 import PrivateKey, PublicKey
from .zip_0316 import derive_ovks
from ..hd_common import ZCASH_MAIN_COINTYPE, hardened
from ..output import render_args, render_tv
from ..utils import i2leosp
class ExtendedSecretKey:
def __init__(self, sk, chaincode):
assert isinstance(sk, PrivateKey)
assert len(chaincode) == 32
self.sk = sk
self.chaincode = chaincode
@classmethod
def master(cls, S):
I = hmac.digest(b'Bitcoin seed', S, 'sha512')
I_L = I[:32]
I_R = I[32:]
sk = PrivateKey(I_L, True)
return cls(sk, I_R)
def __bytes__(self):
return self.chaincode + self.sk.private_key
def public_key(self):
return ExtendedPublicKey(self.sk.pubkey, self.chaincode)
def child(self, i):
assert 0 <= i and i <= 0xFFFFFFFF
if i >= 0x80000000:
I = hmac.digest(self.chaincode, b'\x00' + self.sk.private_key + i2leosp(32, i), 'sha512')
else:
I = hmac.digest(self.chaincode, self.sk.pubkey.serialize(compressed=True) + i2leosp(32, i), 'sha512')
I_L = I[:32]
I_R = I[32:]
sk_i = PrivateKey(self.sk.tweak_add(I_L), True)
return self.__class__(sk_i, I_R)
class ExtendedPublicKey:
def __init__(self, pk, chaincode):
assert isinstance(pk, PublicKey)
assert len(chaincode) == 32
self.pk = pk
self.chaincode = chaincode
def pubkey_bytes(self):
pk_bytes = self.pk.serialize(compressed=True)
assert len(pk_bytes) == 33
assert pk_bytes[0] in (0x02, 0x03)
return pk_bytes
def __bytes__(self):
return self.chaincode + self.pubkey_bytes()
def address(self):
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(hashlib.sha256(self.pubkey_bytes()).digest())
return ripemd160.digest()
def child(self, i):
assert 0 <= i and i <= 0xFFFFFFFF
assert i < 0x80000000, "cannot derive a hardened child from a public key"
I = hmac.digest(self.chaincode, self.pk.serialize(compressed=True) + i2leosp(32, i), 'sha512')
I_L = I[:32]
I_R = I[32:]
pk_i = self.pk.tweak_add(I_L)
return self.__class__(pk_i, I_R)
def derive_ovks(self):
return derive_ovks(self.chaincode, self.pk.serialize(compressed=True))
def main():
args = render_args()
seed = bytes(range(32))
root_key = ExtendedSecretKey.master(seed)
purpose_key = root_key.child(hardened(44))
coin_key = purpose_key.child(hardened(ZCASH_MAIN_COINTYPE))
test_vectors = []
for account in range(10):
account_key = coin_key.child(hardened(account))
pubkey = account_key.public_key()
(external_ovk, internal_ovk) = pubkey.derive_ovks()
test_vectors.append({
'c' : pubkey.chaincode,
'pk': pubkey.pk.serialize(compressed=True),
'address': pubkey.address(),
'external_ovk': external_ovk,
'internal_ovk': internal_ovk,
'account': account,
})
render_tv(
args,
'bip_0032',
(
('c', '[u8; 32]'),
('pk', '[u8; 33]'),
('address', '[u8; 20]'),
('external_ovk', '[u8; 32]'),
('internal_ovk', '[u8; 32]'),
('account', 'u32'),
),
test_vectors,
)
if __file__ == '__main__':
main()