diff --git a/zcutil/fetch-params.py b/zcutil/fetch-params.py new file mode 100644 index 000000000..a31e47dc2 --- /dev/null +++ b/zcutil/fetch-params.py @@ -0,0 +1,158 @@ +#!/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), + )