From 01c19d6f40cc8b2cdc22284177f717787645dce6 Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 10:13:32 +0200 Subject: [PATCH 01/10] Simplify import statement --- deploy-cluster-aws/launch_ec2_nodes.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/deploy-cluster-aws/launch_ec2_nodes.py b/deploy-cluster-aws/launch_ec2_nodes.py index 868a732c..2196114c 100644 --- a/deploy-cluster-aws/launch_ec2_nodes.py +++ b/deploy-cluster-aws/launch_ec2_nodes.py @@ -18,9 +18,8 @@ import socket import argparse import botocore import boto3 -from awscommon import ( - get_naeips, -) +from awscommon import get_naeips + # First, ensure they're using Python 2.5-2.7 pyver = sys.version_info From 9b571f52ceebc9f2985125cf84f73657d099ebef Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 10:55:48 +0200 Subject: [PATCH 02/10] Added deploy-cluster-aws/keypairs.py to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 99e9739c..53a6f5b8 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ deploy-cluster-aws/conf/rethinkdb.conf deploy-cluster-aws/hostlist.py deploy-cluster-aws/confiles/ deploy-cluster-aws/client_confile +deploy-cluster-aws/keypairs.py From 37437e8298c8867d8d499bf0e4fa4718b86faa68 Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 11:22:36 +0200 Subject: [PATCH 03/10] Created write_keypairs_file.py --- deploy-cluster-aws/write_keypairs_file.py | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 deploy-cluster-aws/write_keypairs_file.py diff --git a/deploy-cluster-aws/write_keypairs_file.py b/deploy-cluster-aws/write_keypairs_file.py new file mode 100644 index 00000000..379f30de --- /dev/null +++ b/deploy-cluster-aws/write_keypairs_file.py @@ -0,0 +1,41 @@ +"""A Python 3 script to write a file with a specified number +of keypairs, using bigchaindb.crypto.generate_key_pair() +The file is always named keypairs.py (and is Python 2). + +Usage: + python3 write_keypairs_file.py +""" + +import argparse + +from bigchaindb import crypto + + +# Parse the command-line arguments +desc = 'Write a set of keypairs to keypairs.py' +parser = argparse.ArgumentParser(description=desc) +parser.add_argument('num_pairs', + help='number of keypairs to write', + type=int) +args = parser.parse_args() +num_pairs = int(args.num_pairs) + +# Generate and write the keypairs to keypairs.py +print('Writing {} keypairs to keypairs.py...'.format(num_pairs)) +with open('keypairs.py', 'w') as f: + f.write('# -*- coding: utf-8 -*-\n') + f.write('"""A set of public/private keypairs for use in deploying\n') + f.write('BigchainDB servers with a predictable set of keys.\n') + f.write('"""\n') + f.write('\n') + f.write('from __future__ import unicode_literals\n') + f.write('\n') + f.write('keypairs_list = [') + for pair_num in range(num_pairs): + keypair = crypto.generate_key_pair() + spacer = '' if pair_num == 0 else ' ' + f.write("{}('{}',\n '{}'),\n".format( + spacer, keypair[0], keypair[1])) + f.write(' ]\n') + +print('Done.') From 49710c59588abef00771b51dd809d802f2f7e457 Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 11:34:00 +0200 Subject: [PATCH 04/10] write_keypairs_file.py now writes a Python 3 script --- deploy-cluster-aws/write_keypairs_file.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/deploy-cluster-aws/write_keypairs_file.py b/deploy-cluster-aws/write_keypairs_file.py index 379f30de..719685e8 100644 --- a/deploy-cluster-aws/write_keypairs_file.py +++ b/deploy-cluster-aws/write_keypairs_file.py @@ -1,9 +1,16 @@ """A Python 3 script to write a file with a specified number of keypairs, using bigchaindb.crypto.generate_key_pair() -The file is always named keypairs.py (and is Python 2). +The file is always named keypairs.py and it should be interpreted +as a Python 3 script. Usage: - python3 write_keypairs_file.py + $ python3 write_keypairs_file.py + +Using the list in other Python scripts: + from keypairs import keypairs_list + # keypairs_list is a list of (sk, pk) tuples + # sk = signing key (private key) + # pk = verifying key (public key) """ import argparse @@ -23,13 +30,9 @@ num_pairs = int(args.num_pairs) # Generate and write the keypairs to keypairs.py print('Writing {} keypairs to keypairs.py...'.format(num_pairs)) with open('keypairs.py', 'w') as f: - f.write('# -*- coding: utf-8 -*-\n') f.write('"""A set of public/private keypairs for use in deploying\n') f.write('BigchainDB servers with a predictable set of keys.\n') - f.write('"""\n') - f.write('\n') - f.write('from __future__ import unicode_literals\n') - f.write('\n') + f.write('"""\n\n') f.write('keypairs_list = [') for pair_num in range(num_pairs): keypair = crypto.generate_key_pair() From 32b5ea4cbb54f6fcc6b06f9e340731c02236d7ab Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 11:44:58 +0200 Subject: [PATCH 05/10] Minor edit to write_keypairs_file.py --- deploy-cluster-aws/write_keypairs_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy-cluster-aws/write_keypairs_file.py b/deploy-cluster-aws/write_keypairs_file.py index 719685e8..92e05584 100644 --- a/deploy-cluster-aws/write_keypairs_file.py +++ b/deploy-cluster-aws/write_keypairs_file.py @@ -30,7 +30,7 @@ num_pairs = int(args.num_pairs) # Generate and write the keypairs to keypairs.py print('Writing {} keypairs to keypairs.py...'.format(num_pairs)) with open('keypairs.py', 'w') as f: - f.write('"""A set of public/private keypairs for use in deploying\n') + f.write('"""A set of keypairs for use in deploying\n') f.write('BigchainDB servers with a predictable set of keys.\n') f.write('"""\n\n') f.write('keypairs_list = [') From 17cee6dcae08fccd15e6222b660bc182a489338d Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 13:27:45 +0200 Subject: [PATCH 06/10] Made keypairs.py a Python 2 script again --- deploy-cluster-aws/write_keypairs_file.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/deploy-cluster-aws/write_keypairs_file.py b/deploy-cluster-aws/write_keypairs_file.py index 92e05584..cc36ecd5 100644 --- a/deploy-cluster-aws/write_keypairs_file.py +++ b/deploy-cluster-aws/write_keypairs_file.py @@ -1,12 +1,13 @@ """A Python 3 script to write a file with a specified number of keypairs, using bigchaindb.crypto.generate_key_pair() -The file is always named keypairs.py and it should be interpreted -as a Python 3 script. +The written file is always named keypairs.py and it should be +interpreted as a Python 2 script. Usage: $ python3 write_keypairs_file.py Using the list in other Python scripts: + # in a Python 2 script: from keypairs import keypairs_list # keypairs_list is a list of (sk, pk) tuples # sk = signing key (private key) @@ -30,9 +31,13 @@ num_pairs = int(args.num_pairs) # Generate and write the keypairs to keypairs.py print('Writing {} keypairs to keypairs.py...'.format(num_pairs)) with open('keypairs.py', 'w') as f: + f.write('# -*- coding: utf-8 -*-\n') f.write('"""A set of keypairs for use in deploying\n') f.write('BigchainDB servers with a predictable set of keys.\n') - f.write('"""\n\n') + f.write('"""\n') + f.write('\n') + f.write('from __future__ import unicode_literals\n') + f.write('\n') f.write('keypairs_list = [') for pair_num in range(num_pairs): keypair = crypto.generate_key_pair() From 1d87afab0ad5256e8c9afa55b3bba7ff6428b8f2 Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 14:42:38 +0200 Subject: [PATCH 07/10] Added -k option to clusterize_confiles.py --- deploy-cluster-aws/clusterize_confiles.py | 75 +++++++++++++++++------ 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/deploy-cluster-aws/clusterize_confiles.py b/deploy-cluster-aws/clusterize_confiles.py index 498953e5..d1fcb6ee 100644 --- a/deploy-cluster-aws/clusterize_confiles.py +++ b/deploy-cluster-aws/clusterize_confiles.py @@ -1,58 +1,95 @@ # -*- coding: utf-8 -*- """Given a directory full of default BigchainDB config files, transform them into config files for a cluster with proper -keyrings, API endpoint values, etc. +keyrings, API endpoint values, etc. This script is meant to +be interpreted as a Python 2 script. -Note: This script assumes that there is a file named hostlist.py +Note 1: This script assumes that there is a file named hostlist.py containing public_dns_names = a list of the public DNS names of all the hosts in the cluster. +Note 2: If the optional -k argument is included, then a keypairs.py +file must exist and must have enough keypairs in it to assign one +to each of the config files in the directory of config files. +You can create a keypairs.py file using write_keypairs_file.py + Usage: - python clusterize_confiles.py + python clusterize_confiles.py [-h] [-k] dir number_of_files """ from __future__ import unicode_literals + import os import json import argparse from hostlist import public_dns_names +if os.path.isfile('keypairs.py'): + from keypairs import keypairs_list + # Parse the command-line arguments -parser = argparse.ArgumentParser() +desc = 'Transform a directory of default BigchainDB config files ' +desc += 'into config files for a cluster' +parser = argparse.ArgumentParser(description=desc) parser.add_argument('dir', help='Directory containing the config files') parser.add_argument('number_of_files', help='Number of config files expected in dir', type=int) +parser.add_argument('-k', '--use-keypairs', + action='store_true', + default=False, + help='Use public and private keys from keypairs.py') args = parser.parse_args() conf_dir = args.dir -numfiles_expected = int(args.number_of_files) +num_files_expected = int(args.number_of_files) +use_keypairs = args.use_keypairs # Check if the number of files in conf_dir is what was expected -conf_files = os.listdir(conf_dir) -numfiles = len(conf_files) -if numfiles != numfiles_expected: +conf_files = sorted(os.listdir(conf_dir)) +num_files = len(conf_files) +if num_files != num_files_expected: raise ValueError('There are {} files in {} but {} were expected'. - format(numfiles, conf_dir, numfiles_expected)) + format(num_files, conf_dir, num_files_expected)) -# Make a list containing all the public keys from -# all the config files -pubkeys = [] -for filename in conf_files: - file_path = os.path.join(conf_dir, filename) - with open(file_path, 'r') as f: - conf_dict = json.load(f) - pubkey = conf_dict['keypair']['public'] - pubkeys.append(pubkey) +# If the -k option was included, check to make sure there are enough keypairs +# in keypairs_list +num_keypairs = len(keypairs_list) +if use_keypairs: + if num_keypairs < num_files: + raise ValueError('There are {} config files in {} but ' + 'there are only {} keypairs in keypairs.py'. + format(num_files, conf_dir, num_keypairs)) + +# Make a list containing all the public keys +if use_keypairs: + print('Using keypairs from keypairs.py') + pubkeys = [keypair[1] for keypair in keypairs_list] +else: + # read the pubkeys from the config files in conf_dir + pubkeys = [] + for filename in conf_files: + file_path = os.path.join(conf_dir, filename) + with open(file_path, 'r') as f: + conf_dict = json.load(f) + pubkey = conf_dict['keypair']['public'] + pubkeys.append(pubkey) # Rewrite each config file, one at a time for i, filename in enumerate(conf_files): file_path = os.path.join(conf_dir, filename) with open(file_path, 'r') as f: conf_dict = json.load(f) + # If the -k option was included + # then replace the private and public keys + # with those from keypairs_list + if use_keypairs: + keypair = keypairs_list[i] + conf_dict['keypair']['private'] = keypair[0] + conf_dict['keypair']['public'] = keypair[1] # The keyring is the list of *all* public keys # minus the config file's own public key keyring = list(pubkeys) @@ -64,8 +101,10 @@ for i, filename in enumerate(conf_files): # Set the api_endpoint conf_dict['api_endpoint'] = 'http://' + public_dns_names[i] + \ ':9984/api/v1' + # Delete the config file os.remove(file_path) + # Write new config file with the same filename print('Rewriting {}'.format(file_path)) with open(file_path, 'w') as f2: From 11576a32a1dced19182a9a1457ef54b09162dd18 Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 15:13:34 +0200 Subject: [PATCH 08/10] Minor edit to write_keypairs_file.py --- deploy-cluster-aws/write_keypairs_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy-cluster-aws/write_keypairs_file.py b/deploy-cluster-aws/write_keypairs_file.py index cc36ecd5..6af96e00 100644 --- a/deploy-cluster-aws/write_keypairs_file.py +++ b/deploy-cluster-aws/write_keypairs_file.py @@ -4,7 +4,7 @@ The written file is always named keypairs.py and it should be interpreted as a Python 2 script. Usage: - $ python3 write_keypairs_file.py + $ python3 write_keypairs_file.py num_pairs Using the list in other Python scripts: # in a Python 2 script: From 3595da4deea2b205481ae629ac79588a110b1704 Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 15:15:03 +0200 Subject: [PATCH 09/10] Docs about generating and using keypairs from a file --- docs/source/deploy-on-aws.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/source/deploy-on-aws.md b/docs/source/deploy-on-aws.md index d7efd219..8fd9b840 100644 --- a/docs/source/deploy-on-aws.md +++ b/docs/source/deploy-on-aws.md @@ -143,6 +143,18 @@ That will create three (3) _default_ BigchainDB configuration files in the `depl You can look inside those files if you're curious. In step 2, they'll be modified. For example, the default keyring is an empty list. In step 2, the deployment script automatically changes the keyring of each node to be a list of the public keys of all other nodes. Other changes are also made. +**An Aside on Using a Standard Set of Keypairs** + +It's possible to deploy BigchainDB servers with a known set of keypairs. You can generate a set of keypairs in a file named `keypairs.py` using the `write_keypairs_file.py` script. For example: +```text +# in a Python 3 virtual environment where bigchaindb is installed +cd bigchaindb +cd deploy-cluster-aws +python3 write_keypairs_file.py 100 +``` + +The above command generates a file with 100 keypairs. (You can generate more keypairs than you need, so you can use the same list over and over again, for different numbers of servers.) To make the `awsdeploy.sh` script read all keys from `keypairs.py`, you must _edit_ the `awsdeploy.sh` script: change the line that says `python clusterize_confiles.py confiles $NUM_NODES` to `python clusterize_confiles.py -k confiles $NUM_NODES` (i.e. add the `-k` option). + ### Step 2 Step 2 is to launch the nodes ("instances") on AWS, to install all the necessary software on them, configure the software, run the software, and more. From 675a0ff30b8f0506b4205b565735a083d64c38af Mon Sep 17 00:00:00 2001 From: troymc Date: Tue, 10 May 2016 15:53:20 +0200 Subject: [PATCH 10/10] Commit a 128-keypair keypairs.py file for testing uses --- .gitignore | 1 - deploy-cluster-aws/keypairs.py | 264 +++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 deploy-cluster-aws/keypairs.py diff --git a/.gitignore b/.gitignore index 53a6f5b8..99e9739c 100644 --- a/.gitignore +++ b/.gitignore @@ -73,4 +73,3 @@ deploy-cluster-aws/conf/rethinkdb.conf deploy-cluster-aws/hostlist.py deploy-cluster-aws/confiles/ deploy-cluster-aws/client_confile -deploy-cluster-aws/keypairs.py diff --git a/deploy-cluster-aws/keypairs.py b/deploy-cluster-aws/keypairs.py new file mode 100644 index 00000000..f10be7b8 --- /dev/null +++ b/deploy-cluster-aws/keypairs.py @@ -0,0 +1,264 @@ +# -*- coding: utf-8 -*- +"""A set of keypairs for use in deploying +BigchainDB servers with a predictable set of keys. +""" + +from __future__ import unicode_literals + +keypairs_list = [('E72MmhHGiwywGMHdensZreNPTtAKvRxYQEyQEqUpLvXL', + 'Ar1Xt6bpmeyNnWoBUAAi8VqLPboK84bvB417FKmxcJzp'), + ('BYupx6PLnAqcTgrqsYopKeHYjmSY5F4rpSVoFv6vK3r6', + '6UkRsEhRW7RT6WYPJkW4j4aiqLiXhpyP7H1WRj2toCv3'), + ('A3FwThyWmydgjukcpF9SmTzWQ4yoRoV9jTni1t4oicz4', + '91cuZ3GvQkEkR8UVV456fVxiujBSqd9JMp7p3XaHnVUT'), + ('CkA7fS6aGmo8JPw3yuchz31AnP7KcxncQiu3pQ81X2Mj', + 'PDuBGWm4BnSSkTTxSWVd59PAzFqidaLfFo86aTLZoub'), + ('7aoKCGN4QK82yVpErN1EJyn8ciQXBUkVBe1snx7wypEf', + 'AXoar7qdJZF2kaTJb19PhqY7iSdX3AEef7GBwb9N8WT6'), + ('1GGrwAx34CbTfaV55KdCKah2G5FThjrTdTQk3gTD97x', + '853HbGVt6hT7q17cN6CtDycuTyoncUDWscdo4GUMKntp'), + ('7C6BZbk3Xi4nB1o4mUXMty4rs22CeF8dLQ2dUKhyi9qs', + 'GVu5QTqKeMGhgz8AgzfVRYP5F3HopkqgabQhRqjujEdN'), + ('2WXPBsMGmwjMv7Eg5iqgLAq2VQW1GF6AVAWuveYLXy3Z', + 'AuBnVm277newtkgfyjGQrr3X8ykKwzVzrcpqF67muQ4N'), + ('H67sRSm8W6gVmR1N3SqWXF3WTx9Arhc1RtwvLEjmhm9t', + '5SQamPP4dWUhu2L247TMnf8vX1C5vuB3jtfh1BpVSsPg'), + ('GYztiuCLEvG4wrVszbXKs9AXbKBbDZVhw35xsq8XF63S', + '6pxa9WydnD1xRFReR1yHruXL8VtFu3c6kCNBXkwAyDXA'), + ('G7x9iHnJkjKkdLEsrV2kGZ7tFBm9wj2Pive7vRZss47', + '23MvXauT6cKMLrNyxN41jnZv83aKghLP4B37bvemjENa'), + ('3MhdzHYRrFrPmQZfXfpNKLw9xEdFNZNfUcehgBmboH43', + 'Buqfw4nFfuLoHJcfYZvxXBJf5rTm5ypSyuJfL11jDFkn'), + ('B2MbWPXDPSWQAwNirepSws7a4sgKNCVtmduGh5f54Koq', + 'Cus791pRcuoVJe24WME2QYeEAX1R4uiTGNxa3HwzwtQY'), + ('7yHSmHHX4WwsZ4H6oQxxytkGRqKPiMdqftSvRqXiomYj', + '2qVE6baeD57raXJnNwyUeWi1VyfpQ21QW1J374zMGD6o'), + ('E2V7mzxce6J8PZw8rUEZXYYVnTFRkMSfTty7duohox6V', + 'HSs1oWnvTfjrMmVouRtFJYLjfgeC1uxEiA8MX9F98A34'), + ('4yP4RH18nt3DDFzhpLGborEJuS7hx4cKaz6AAe1xNChe', + 'FziConq7CF4h6TYc1W4wYtmJbhNnrAGoareRkeoRLKTi'), + ('HGgVjtNG2U6odxbAAR31UAcHknzenY88GxYhnURy2S5C', + '82miL67GzT9fTVt8hFiE2XJBRr7iNXAvFLnuiFj5HyjV'), + ('AWY2DyCDbMQqx6v5KtcoW1f9qQd5NqiibeLFpABwibEn', + '9KgHN7xTLa34hfwGq4RpW71jsKjyPKRtaAdAvjHuATtb'), + ('BYE1oV6Dyf49Qedrtf3UaVny9D7NEUhtx78pD1X38M6x', + '3ve8upjPmX9vvdEqvir7QBxnXQAyBWiZKwWyEhq47ptx'), + ('BiZLPsA8Q3faqLPxrcMP1TT77XUYd2jceAkuB9bFCzUa', + 'DrL1j2ZXLvBzk2TmA4DxsRmoR3oCSpW8YPvDCMCei1dU'), + ('FNPkTYojwJ4F4psnzbWt8XnNRBqRhwHXog8Pb8Kuuq7V', + 'FRxatYaiuuKBtvvERSADKNtSGPDY7iPzCmMaLDnPSuG8'), + ('2LiAeAJHjGsrkUmd42jdgPAyKEfzWVttrmnXu6rzfyYM', + 'FwQ3jTBnJpY62paSLdudyymZPUDSWy3827wY13jTJUmC'), + ('Gcu8TPtFM2tv9gtUo5yQYyBg7KHQWxnW9Dk3bp4zh5VC', + 'G3UrGxBB4UCUnxFNKrhmS1rpj3Z7bq484hZUUfNqprx1'), + ('HQGHpzMDdB3sYqfJJC5GuyreqaSjpqfoGytZL9NVtg8T', + 'GA9eu5RDuReBnjUcSSg9CK4X4A678YTrxHFxCpmWhDjM'), + ('2of61RBw2ARZcPD4XJFcnpx18Aj1mU4viUMVN2AXaJsE', + '3aDSyU3E5Kmn9emoUXrktFKH4i7t4uaKBmHNFFhErYU8'), + ('J8oF1sfJzXxJL1bDxPwCtDYw1FEh1prVAhWZF8dw1fRa', + '2atybus8CnehWNVj1DcCEidc3uD2Q7q4tiP5ok2CuNSD'), + ('AxMvjM1w3i3XQVjH8oVVGhiic9kgtvrrDzxnWKdwhdQo', + 'DXYvSgETSxy4vfU2rqPoZFumKw5aLWErkmEDR2w2sR7h'), + ('GBuyEpUQTf2v21NAKozUbUQnwwiugHNY9Uh2kPqBwqXK', + 'CLDPdckwDKa3qiLnoKuNFW8yHRjJdU37XE6skAmageJK'), + ('Bc8ykuXeq7HutQkveQfYXQ28BbFkjRpZCAEuRsAMtxuF', + 'B45qxKWDPnoE1C5KzunsMvfHmRgZHfz2LzxaM1LTqVwF'), + ('9H9v7uKAWScvy34ZQfWJW2NoJ3SWf2NuaqzabcaVuh4h', + '4Kj9wUpHKfgJbjyLNmMYwEwnotUmsgTDKMCusHbM5gcz'), + ('2kWx8nor8McDSZsg8vJ7hZrc3aUVtZhcVcvNeT14iSFo', + '3S9ase3dQd5oz3L7ELGivAsUyaTosK9C5X1aiZNtgcwi'), + ('ENEDnokpqJhziw9CPiGDCnNRwSDgnGjAPh1L7XABWP6s', + '2sUKDdtfVaUXZCN6V6WecweBL8ZEY5mCfPBTj4xzhQtq'), + ('FPUYgS4VvQ5WaZaQqnrJppBZQasoSMwZ4LyhUBKYnE6Q', + 'FtP6Zak6EEWpuptqxSoPAySfm4yA6rWAQqxMCi6s6RYp'), + ('FhQjcEjy36p27YGjKzWicdABNWzEYGciSU5Eht98o2eg', + '2hZ3Fby9K5jYQdtrhvehKTeJgq4NDJY46p4oBT7NUAv5'), + ('5JD7STAtYDUeMqvA75FxTGUw6mSFmFvnVMJZJkTHLafH', + 'HCGf4nWF7q4v4GBPXxTdWMjU7o3SifxfmKzTQ1dWmFqo'), + ('3VLPrCmUog6mBVqkTuSJzXP7ZABCA6ejQKu9LpzkJs6s', + 'Bap6iTjmZb781zLxSmELoqVA25mbMuL7B8WdAz5wDygG'), + ('EiQ57ZLHQvtLbEbiJ41ViZmPctFfd51EFEaK6Y3HZcYb', + '5uu84u8um1CfuT1pvpdFKMy5oWuU4BfWRbpRHzG4eW4A'), + ('3hM9hy2LSqe2SsrcE7XeNz1aPPPZgK5bnTeboyFgFsyj', + '3ptDB8YwcU9EiafreJnFSyfNuoKMMws7U7auMadhRxdr'), + ('3LoFwupCNbPk4cMYVS58UHtkDhvKpdYNmMJPEbK5hnat', + 'CQ56mX3agjJoWwt2uDSa7gtzHWyg3y4Lqp16rZL9qUdF'), + ('F9X1XsTcFwJN196D1PdCc88WrVrBGhfDgQnezeXW9Vjx', + '79cg39iLMZHPFbXrWo6aJAbsXFPk7kgqgBxijDbDLKA'), + ('Hf1XCRfcXc6sQZVchcvv54Sod8BjBFqsiU5Wu4eX6bTd', + '4o8pJV5jaNVqbQhw1u9EzoHT9m69bkfDSGVGugBYeiPC'), + ('2hamLVNSruGH8uT7YvXa3AUcsspg2ERqFqT11gbKpbUK', + '3SziPezcFQbhPrGVJrm5D8HVAZSjduBhFanaXBZcGr3s'), + ('6u92HEbihHiorTANWBs5nYsHJSJ21SfSqsD4FwZy8UZr', + '9jo5yogiEVYwxCkzYoHrn7WMnxpRqqJxbAFuMA2TuzmW'), + ('4YJJNsfEz3eiBE48w8kihENuwDXGbS1vYLi27663EDvw', + 'xcAieBttVYi8g9NQBBjf9jPoaMoWx3hA1h3iCcB11jC'), + ('CUSUaZiUyy8f9yf59RSeorwQJGGnVgR6humfvmzpBMmS', + 'EbR1dthGhu82wPJT7MmqKu84eKNKQXEuUm6Lqdf4NLXu'), + ('5RBfhrADkYu5yFKtgdVZPq1k78VcQc3VZr4kjWpXmACs', + 'Ev4PviNfb87KH5HSXEj7gN7uBYLbHWFSFqQPsoYcMHK7'), + ('4M4UiTmPLY6H4AhSkgUkKQ6cRceixyL6oT86AUvK9tTs', + '4VuGTkZ62PbgKEotnMtyG6L2M76v3qabhPZKXeJ1npca'), + ('BDAWs8i2GbRySDC5SCStzFdEvnfiqCTEbu9mpZRoKdA8', + 'FoyMqh9tcY6xCyLxdByrW8xgzAqGNJKR9dPEN7CjPmQ2'), + ('Dm1HwCxzLm76hBGAG2NEziNRiPBiYnQoKivPm5JC3477', + 'Ap747d6xaUofhsFoyXbg7SCpH53tsD8zbLY39QS2jWfC'), + ('6dRpaKGL3pzaoBX1dKsduuDkkPfUB1yBb1taCYZoNGw2', + '7PoRrQTBXmCkKuwvLxQceBbUwqo4eReNTxVaGVT6npdn'), + ('Cb6sghYERbQA5VMdxKiZx1xk6j6heJNbW1TxRTMwkquu', + 'Am8zvPbAgk2ERqmhGzJZL1NCNkEUjF6enXCwczp4d97B'), + ('EhaLhpbbRCfCuLbq3rQS1d4PfE6rHgvYA9MpTGaxACgW', + 'EfeeApbq1jBChfhe13JkEPbUfm1JYYFCdKXdtue6YrH5'), + ('353aMTUrjH628XzVnKH2oyRmMaAdJ4antn5fGNAzfqMN', + 'AqustPmyDtVpFDiUEqWfFjDeVBQhvKYZFU4wjfpXRXee'), + ('7x8v2BEkdyDvzVzbRJR9AztZHLv8kUZfwRRmcPEpHEYj', + '88MTxTfy7Btqxwdf5Xo7TmjzACeuNop8MeE63LikQn4k'), + ('2jnPZg4oeBzbqL6TdpyTdoxraqjWHqfSrzfmS5Qh8D4V', + '3GSJUg4s6ydymn9obTxdSVBkxpmWZLCGuvBK9fMECefe'), + ('N8DS5DA18i2Bh7rEz7nJSSJZycz8qKaNkPP58BCh7Zr', + 'AKjy7whpaoUnbDJXzNHbtBHawWnS7tLha3nfMPXh4Qam'), + ('DUQ3pGX5XQtvucPDcNEQPMLrqCMxCbRBuWmHHddNg83Q', + 'F3vakqePy8xmpb23psZahDVEdu4dywCPQB7fCMsP5mp3'), + ('6ABw5HQZSWWJr2Ud6KmD73azu732iNTvEfWbCotCFLrn', + 'GW9eq8JgkHDLjtENDscTK5Bj9AAC3css7SPxLZCPcS2V'), + ('ByNJL8Eo8B6kKH5UuJxiXBRRrAKfALLvQmt2Rq5JgAA4', + 'GEtT15SrZUDxVpLjS4metu4BXYw4o1NmxzH5Wr2DcqAv'), + ('F9XaoqP4A4zZoPB6phfTP8i7CQsnSakh6bk8B1CTLwqy', + '9XLZaFGco78AXQS9dmHZ6zypjtg1Z33pj4KoTtDmnLa6'), + ('ESamPv9kb87uEBZjfgarKTQwSTcEQMetBH44b8o3mPZC', + 'Nv7eXkgL84o8fQLjagN1bVj7bt5FKF6Ah1Md6WWwyLk'), + ('E43hqzYjZZ1XRSME6d37Q99UifT4d23piq1CN3fMp6cv', + 'HLMB1uPdRuYcQyM9UmY9zerxQa3cYqEaRUku3h9oRBQn'), + ('3qfPXUTeCsVRk9L68uyGF5u3XxFSVBtPkATtHayVgCGs', + 'ZEkiCeoj3FGfudrN4xgtbz1VihkKWm4cgHN9qJ4p4GH'), + ('7fxCmzKhvNGpbn9U2vih9N1aF5UXaVER6NSpwn3HPpoy', + 'CmhLU67kWqbL2kyj8bA5TNcg7HiQFJeamcJhe5BB1PAx'), + ('BhJsfuvhj9PqfvnvNGQX26fR5SXvcq7JdhhrWyZHoXT9', + 'CgMqrhrjr4mBMvTgiHLqgvf4tRzUpZuLtQnMSG1Jjgx2'), + ('GZbkL2W22Z2YwHf5SBwRfSEYQf1tquPkELDQjkwm2yU4', + 'E47ijUUheN1Zz8TWKmbcDDWz5nduBvZNtcgqbGRiiGv6'), + ('9Puc7H9PRHZ2oowzxiuGueZCzNY1X3aSuopy7k4w8TTo', + 'FTjTVxsPjiNw6TnbwBeE7WpZbvJuVEMwbdPCt1NppHhc'), + ('BczGQKaQNu8QkTc4PWmPdrbLfmXFzAqnoJ9YzHTU1vez', + '4m4xe8fjWAFHyNYLMRYDXskG2d5o9xZxgzCzca23uBBH'), + ('BZwZrE1hNzKzfnbThE9MiB5Movox67c7uGJmi9Nhef1j', + '5G6reNxH3e1gyMSgBRCYQJypFtTSBQ85r5fQGw6DfnpM'), + ('DFJxcvaR5Xk2bHiuxZzaqDxLDSq6fGSUdg54c5zAFLKz', + 'BRL9LWweehDAcEPc8MXjd3uQtAt4ZK1LY8J5KT3GeYKm'), + ('5wfyCc1mAhp2DCKMmEQG9nW4bKfaVkk8kpjuerApiFXv', + 'rdqo7bdePrF6wR8v8dzJopEHgqNgt2yNmMjxz6wMguQ'), + ('8S42sTQQqr5LJTa6jBjCfNg6xvjeL95btPJt2MPHBrDo', + '7VJjwATaownwJyTWXJxKEtJk46eEXTm9UaioPvVFD2zD'), + ('57WwYQgHHSu7SYrXXmovjiPDmc2BB25itp6xSu5KrQQn', + 'FGW86z4ymEbtqiSpp6zzpDkzdPZv9xDMCGUdGVBz8KLU'), + ('CcxnCDQ4JgH2ceTEPW75GcfW8rP7aiAT8ZuEtYbqEa7w', + '7kQdXRZNJaWo7Gj4XtT1fV4LD4ZtN8VmxdZFiJE8q8xF'), + ('8CYTgLp2kbVJKqnadQNGZorWcdWNpbaXrt6kvdzJnEjv', + '57Zwyf4FUEWTxEWrmbSb6vrcZBukHmCs7TKzKoygV6cf'), + ('4buY9tDvVRpTjfAjM8Up4vWw5yh37xWteSMAerbxpKpv', + '5FvFDCSZgtc57hSpvBqBd8VjhyAJ2a2vxTiHzg2nPyg9'), + ('5jJ8hry8Pu7rkgKkWcmZzfZ5FWk6rT3TnYGesEhfijvt', + '7hmVhrQ8vmHmNhxyvyW1cHF5N6gzRoBy7kimfj4b2uZ5'), + ('6MUnCTEZFZvsKTCW4NKDiPv4a3SRWZLS7gUNP4nXsFBh', + '5m2oXtepVwbKt9t5er72bFNioiHYMRtHcUu176DVFBQu'), + ('GXuU171dpf8JpBLiVgXksyXrdkqDqm6AWJ5A2JZLkkwV', + 'BF6xtHg3kcBKHCJ9Y6TTPyGYn3MDKLqxVDshVUbgaCAk'), + ('DoRUYrhULJbAnsGot4hYZeHUaFgXj4hwhHiGRUP3rZCj', + '8i67E6uPyrRvAN5WqSX9V7xeSGr4nPXqAgnR2pPQj3ew'), + ('At4gvM1wZt6ACte2o26Yxbn5qaodMZBd7U1WsiBwB42x', + 'GBPGPkSkkcM4KmJRqKjCPiEygVLW8LmRRarYvj967fbV'), + ('48D3mw2apqVQa6KYCjGqDFiG5cbwqZEotL2w8aPWCxtE', + '2Byvg9DGK7Axk9Bp6bmiUoBQkkLsRNrcoq2ZPZu5ZyGg'), + ('2YncoUMac2tNMcRFEGEgvuDXb58RdyqHMWmSN2QTMiCP', + 'BSNXYAX8Em2TjuCDvmLV3GgnxYT6vX68YFwoGPaPpsSa'), + ('7As7DVaC6FBqojvFoTo9jgZTcTGx9QYdVwUhNSNNvUsz', + 'E5cMypehm8j2Zxw3dCXKc1MGTCftJJm9FzqPNwnVEgQn'), + ('AAwj9V5iW88EwoZpLwuRvqwKn8c8rSgKAyZduFAfvqvV', + 'CkTks2ZGnQdM19wucQMehPe1imANjnRAAfLd1uewGUx8'), + ('axH9mijksto4vnoTjbbJJfY8iBFziJL2y39ago41WNM', + 'GJV8hxcjpieuXmVz9j5JjB2eFLXmRoBF7EYWpbMNKq7Q'), + ('6vv2FyJcTNJRnEmsRgHai5Bf7trJ8CsBMqbZckXWkbGk', + '5YXtgt3ZVKKYM3qvHXXKKSpStfH38akRYvg9shNNChWS'), + ('DKK6kfAGnLV1mowm9m52yYFViVbQfVEtmRuveiXbnC93', + 'YvrVGNzxXSTLQ5QQJ3GHWHDQJnd3qJ5npGQQvZtb4m1'), + ('4QWSQeeu9oQA3ZQG7d6LKzZLR3YZ79q999Zzb7hb2cbh', + '42ARr6nFsZXLAgGGwZ5p55kVSW5ETjrnJBUxaV6sFmzk'), + ('43oJ9CvF3Wsymj8zrkC19VfzjMiwntw3AXrTvc2UFuuf', + 'A661APGeLXuLgYUwmQjKWnuz1XmjuLNW8XVGuGjmEm76'), + ('3uN8UwhNcg219uX1GffC3a9tCZrVY327ZUk5rs3YfAR2', + 'Ca5B2Z9PAeBkEPuYeUyvs3dHhTqpAzFuXERfHZT3zxto'), + ('HuV5FPtboYQe2EEVFVhBkjRxbUBjeBCHRk2VuiNnBS7N', + '5AJCbvgfLmdGdWKjLpDBZtrrJC6NNCQJK5P9NmpvbByy'), + ('2Rbr8Lasv1CDhL2Xxu5ZfLHf4fhCfxuTr25YDB2Q5VXN', + 'FQTbtsHjw1oYyKF3pUamwubB27UqG1ista1ezL2kgF3N'), + ('CLGF2xs7YyJrNZ8ertsPwofzqTBfQiJ5cMiRNcMjgEkh', + '4uSue7UmSr1H8QCYrerRRyUh2BTqX5t5qPWRdVrcyL43'), + ('o6jUu8mqTQMaawxRBbvuWd3b7syXYEUPFWJGuNuoDs6', + '7uJuBMMZD3d6mq2ihUtJQLWsAqACAkmQSJ3gUcEgW18W'), + ('2wo2o5rqEEyijwm4MuCXHNVp2oJPEYQBF2eU6CoXYuVy', + 'AZY2HCpLGjsUgKo7PZ6gdx6btReR6gRCeE9gmzebgGZ2'), + ('Eo1z9xyGbHZxH1ezG7iLxJFhuL8YWJ6NREu4T2VtRZky', + 'GbjDtbwPBf6pcczRbANBvHeBNb3obMtEMoQTxmmafq2g'), + ('8oPaUg1Wc7293c8HR7Phs4m4DvzDjYuzFUBqffJUhJKP', + '9vJKX3jgc1K4sdhnVYLhU6iv7vf8mRygRDYr784mYUpp'), + ('K2BCZLghAwL4Y9eiPboQM2sz4GWYFM5WApZT6firnig', + '7j9QMXcyqgeVFejyNMhXszKAbZuNdECFYwZFDNCwHN3V'), + ('Dz8Ft3YeeuMcsPKMWNqDDbdx6Qo2s2H2cZNUoX2uDwgY', + '3HqEP9EvU9852orfSh9WZd54pDMJnT5nMnGkjhZibbZg'), + ('3cq9D9s9vZgyDertxiZr21etinCYKCMYcf3LXe3o8zT4', + '5174KhHkMsti7XNSYh5j1jFEv22PHQQizTXxT7gT2ZPb'), + ('5uJwmzmoZDkADaeyenBvceP4mSzBgEgbqU5cc8JQpTDE', + 'HEYiTYWaTwjXkzfbE4eZ1RL78ciJkWqEio8tDTvCXzk8'), + ('BkHzLwC5bkLVB4b5KPAqbWc4ekhqmMtk34tfYpLQ53KR', + '537uFsVdCU81kSG7eUZBFV5q3PvadsS4KgzaLuGWGzgG'), + ('3eQT6nC2BEqtXa5b5dn51cJEpj4eMHYsx7RkHXfwNEkq', + '2NV1QhXppRfj19ZemqGUgxZ9Pd5yD13aQmrcNd6g25D4'), + ('GsBGHmKMiJoYDhoXJXwUnkbH4cVWWQ7emG1t7vTFDdS', + 'CsLyGG9J9E4ZLwhpTHRgp21tvGWyPj79SaLGEpqVhHKj'), + ('ALytZ6ygpy3hqHVXGHHdNuzuQh1hSoTVU8im5C6CgTR2', + '5646BEZkpyoDWQHMscMav8bXoiAzf7giVmu8yepWsoMN'), + ('5XhJnzEfqVRM6trhL19K1AoGAQjbWC84Cv5XZ4nE9fF7', + 'BJdQwVTx2fuJWkStt3yPD2WUeopjV3yPQp1646Yi2pXL'), + ('7XLiDAjnggSU7PAvrTwsyPebC3bhuc5B2CMdiYAQBGWZ', + '8xnXGiNp1ADNfuG6uLQ91h2h1ekjuiEC5SRdw19rbpnq'), + ('7kyFUtCcaiWKfGZmWfb9kvwcYLxxmocBC7qXYwNwotgV', + '574EqNs3exLKJxgqFxKyLE5XQMBkadQf5MKQ8qpjsVJS'), + ('ESJSEPbWb13NaDkde8rEdcippc58AMCZodfmJP1SK16m', + '5iwWfDDjgyFfeLpS9EYmwszScwtxTACcgAbinCjFLZTZ'), + ('AjnWLT2vZnEmLfioGeseLuxGQFFiyoqtFJj2oEUgzax5', + '9JeUGkGHPyB7s7XVVik1aFyCxarH2tWhpSJapnRXveb8'), + ('32yM1jbRpZt7EjnH2UDimusAPfMQ83Wd1AULxLYMv2hq', + '73v6uEUhL12MEwdfFFDmqbWmSQXoC8Y3VPB9vKUYEW5X'), + ('F5DjMdHvqqym53MtBG1v9shrza74EttHn1zPFL1ic1hT', + 'FpkXbvZsW4LbU4XZYvy6euR7F9SxDMPdyVVCfJFUaT2C'), + ('3EPdMUSAXFuQLaVwq1fPHNUPzvSHXqfNupgu6kGhdEVc', + '28RxZbx71Y8ZaYt9f9D2HnAhkH2CvAPT4PXpDgCnXhVY'), + ('47YXW4Escn71q7xf6qip8NwdKTq2ScL1i4xmAnJ1RvDW', + '3NQxT4ukLvPPZV3J6qDmx5PFPa7GvaiMBwc1r47SXdfj'), + ('AiCfcc6viFsxTxfEJxo82b3GWzim2nRXvBBfB14w4dMr', + 'FBCcBLpFUss64MWjf3nuSRrLNoqnWpJGfXKJVaduPezJ'), + ('CkeGi1XM3nquJcp3osb2EhTJ99gsisPfTpnsQdYViWWa', + '4L12aHJtN96XGrYbhBFhmEQuPTnsHu95NATsz3X2Uo4Z'), + ('A78PS3MuQtWQ937ow5mzHhXUS1LNSzX2nMcmqLN57c3G', + '87T6viSDWX7Rrw2VWsqEXhwVmrsrmf2rjDHRkeUGU4rX'), + ('2SzYHP21J4KXwVgSwtNfDQKUbyC7RE8feAwfVuW7PSmD', + '4NCA5NxnhxPAAcWqyxtg4us7MJYSbn8g3Kw6v35Vmnm5'), + ('GxGuWY5A1ADiXFrdCiAcVJX4foveGxDfhcJd2Yirg3D8', + '2Jjo3w5gQ7TsQaN2N7iNejfGLjzucaNg4hYZBcwT7AzC'), + ('5dYeKTvxfH6s9Esbys8TVMDTZMCzjFJAH4xe623ykmZ2', + '5q7Le5Kcm1eBY1r8XwEseDXnEUKkZE5qtNb6p5BSSKwz'), + ('EkbeQ7eoiHxiTmq7ksw6FLvf59b3pGuoDR9LF29KYw4m', + 'CDpJ8VmgiBvYUcZMcPYr3B5UxSVEtLxRfq5dH3AxboNT'), + ('2zXT2EUMwWKPMWHK5rYvxgLNdmkoedXH754uzUBphaCE', + '5oHnEFaUaM1QRZjV48K1DrqKeEdcbmb8uG2zucTYc5qH'), + ('H6c78e97srwPEg5PsW1uuKAovSxTvmNyFt9qJwoeJP4y', + 'inwncuMiPRuw6PEucVG2Kempk91yq3dT5kpuf3Umf4j'), + ('6yJDrenNeRBpdQxqxMY3C2V6cBrfvpzYpz6MbefxuxsZ', + 'CnCjmTECDrqJP5nTPSL2NWJ9LPyyFzLmrTYiRcSjwU7e'), + ('3YTX3ntzsjG9CxbkCayToGEzmn1Fgdvw1W8gefCUTa9L', + 'FkCbQBoKRZbndsNP44CWheEchwPC65UNdrZ8FntRTyvu'), + ('8Y7xgZ5M8qBYdX5iCHe7mPQ6ZcQNXDJd28ZVDdx7FSBa', + 'AYTdxj598H36RGmBzEnR4QK8pVF6k5YTRBypxWsDkXUB'), + ('AtzLLpKuPehdP4g6x4J4BH2RjNbvXewxf8ibSgKSiJtL', + 'vC8C3u71YueJcUhtyfn9Xx5PjpJuizDZNGW23tFb5VY'), + ]