#!/usr/bin/env python

"""fetch-params.py - fetch Zcash public alpha keys

Usage: python fetch-params.py
"""


from __future__ import print_function

import hashlib
import os
import subprocess
import sys


urls = [
    'https://z.cash/downloads/zc-testnet-public-alpha-proving.key',
    'https://z.cash/downloads/zc-testnet-public-alpha-verification.key',
]

sizes = [
    2007057094,
    3478,
]

hash_algo = hashlib.sha256

hashes = [
    '7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817',
    '6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41',
]

params_readme = """
This directory stores common zcash zkSNARK parameters. Note that is is
distinct from the daemon's -datadir argument because the parameters are
large and may be shared across multiple distinct -datadir's such as when
setting up test networks.
"""

fetch_params_info = """
This script will fetch the Zcash zkSNARK parameters and verify their
integrity with sha256sum.

The parameters currently are about 2GiB in size, so plan accordingly
for your bandwidth constraints. If the files are already present and
have the correct sha256sum, no networking is used.
"""


def which(name):
    p = subprocess.Popen(['which', name], stdout=subprocess.PIPE)
    p.wait()
    if not p.returncode == 0:
        return None
    out = p.stdout.read()
    return out.rstrip('\n')


# Note: This assumes cwd is set appropriately!
def fetch(url):
    cmd = which('curl') or which('wget') or None
    if cmd is None:
        raise OSError('curl or wget is required')
    options = {
        'curl': [
            '--remote-name',
            '--insecure',
            '--continue-at', '-',
            '--location',
        ],
        'wget': [
            '--progress=dot:giga',
            # Note: --no-check-certificate should be ok, since we rely
            # on sha256 for integrity, and there's no confidentiality
            # requirement.  Our website uses letsencrypt certificates
            # which are not supported by some wget installations,
            # so we expect some cert failures.
            '--no-check-certificate',
        ],
    }
    opt = options[os.path.basename(cmd)]
    ret = subprocess.call([cmd, url] + opt)
    if not ret == 0:
        raise OSError('%s failed with status %i' % (cmd, ret))


def verify(filename, digest):
    hash = hash_algo()
    with open(filename, 'rb') as f:
        while True:
            dat = f.read(hash.block_size * 1024)
            if len(dat) == 0:
                break
            hash.update(dat)
    return hash.hexdigest() == digest


if __name__ == '__main__':

    if len(sys.argv) == 1:
        pass
    elif len(sys.argv) == 2 and sys.argv[1] in ('-h', '--help'):
        print(__doc__)
        sys.exit(0)
    else:
        print(__doc__)
        sys.exit(1)

    home = os.environ['HOME']

    # This may be the first time the user's run this script, so give
    # them some info, especially about bandwidth usage:
    print(fetch_params_info)

    # Now create params_dir and insert a README if necessary:
    print('Creating params directory...')
    params_dir = os.path.join(home, '.zcash-params')
    try:
        os.mkdir(params_dir)
    except OSError:
        pass
    params_readme_path = os.path.join(params_dir, 'README')
    if not os.path.exists(params_readme_path):
        with open(params_readme_path, 'w+') as f:
            print(params_readme, file=f)
    print('For details about this directory, see:', params_readme_path)

    regtest_dir = os.path.join(params_dir, 'regtest')
    try:
        os.mkdir(regtest_dir)
    except OSError:
        pass
    # This should have the same params as regtest.
    # We use symlinks for now.
    testnet3_dir = os.path.join(params_dir, 'testnet3')
    try:
        os.mkdir(testnet3_dir)
    except OSError:
        pass
    os.chdir(regtest_dir)
    for (url, size, hash) in zip(urls, sizes, hashes):
        filename = url.split('/')[-1]
        print('Fetching', filename, '...')
        if not os.path.exists(filename) or \
                not os.stat(filename).st_size == size:
            fetch(url)
        # Now verify their hashes:
        print('Verifying', filename, '...')
        assert verify(filename, hash)
        try:
            os.remove(os.path.join(testnet3_dir, filename))
        except OSError:
            pass
        os.symlink(
            os.path.join(regtest_dir, filename),
            os.path.join(testnet3_dir, filename),
        )

Implement the fetch-params.sh script in Python.