github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/tools/airdrop-setup/airdrop-setup.py (about) 1 #!/usr/bin/python3 2 """ 3 Test script to be used for creating local accounts to test the airdrop. Note that this will only run against the local 4 keybase dev server since it uses the `keybase signup --batch` flag and relies on a dev only invite code. 5 6 Run via `python3 airdrop-setup.py out.json 1 2 3` in order to create: 7 - 1 account that has not accepted the disclaimer 8 - 2 accounts that have accepted the stellar disclaimer 9 - 3 accounts that have a starting balance 10 Data will be written to out.json in the form: 11 12 ``` 13 { 14 'no_disclaimer': ['user1'], 15 'accepted_disclaimer': ['user2', 'user3'], 16 'with_balance': ['user4', 'user5'], 17 } 18 ``` 19 """ 20 21 import json 22 import os 23 from random import SystemRandom 24 from multiprocessing import Pool 25 import signal 26 import string 27 import subprocess 28 import sys 29 import time 30 31 import requests 32 33 # The maximum number of times we will retry if something goes wrong 34 RETRY_COUNT = 5 35 36 def compile(): 37 # Compile dev keybase 38 print("Compiling keybase...") 39 os.system("go install -tags devel github.com/keybase/client/go/keybase") 40 41 def secure_random(n): 42 return ''.join([SystemRandom().choice(string.ascii_lowercase) for _ in range(n)]) 43 44 def start_service(home): 45 proc = subprocess.Popen(f"$GOPATH/bin/keybase --home {home} service", stdout=subprocess.PIPE, 46 shell=True, preexec_fn=os.setsid) 47 while True: 48 if os.path.exists(home + "/.config/keybase.devel/keybased.sock"): 49 break 50 time.sleep(0.1) 51 return proc 52 53 def kill_service(home, proc): 54 os.killpg(os.getpgid(proc.pid), signal.SIGTERM) 55 os.system(f"rm -r {home} 2>&1 > /dev/null") 56 57 def create_user(acceptDisclaimer=False, initBalance=False, retryCnt=0, *_): 58 username = secure_random(16) 59 password = secure_random(64) 60 home = f"/tmp/airdrop-setup-{secure_random(32)}" 61 proc = start_service(home) 62 63 try: 64 # This is a special dev only invite code that can be used repeatedly 65 os.system(f"$GOPATH/bin/keybase --home {home} signup -batch --username {username} --passphrase {password} --no-email --invite-code 202020202020202020202020") 66 if acceptDisclaimer or initBalance: 67 output = subprocess.check_output(""" echo '{"method": "setup-wallet"}' | $GOPATH/bin/keybase --home %s wallet api """ % home, shell=True) 68 assert b"disclaimer" in output, output 69 70 if initBalance: 71 api_ret = json.loads(subprocess.check_output(""" echo '{"method": "lookup", "params": {"options": {"name": "%s"}}}' | $GOPATH/bin/keybase --home %s wallet api """ % (username, home), shell=True)) 72 address = api_ret['result']['accountID'] 73 assert b"transaction" in requests.get(f"https://friendbot.stellar.org/?addr={address}").content 74 except Exception as e: 75 # If something goes wrong, just recur up to RETRY_COUNT times 76 if retryCnt > RETRY_COUNT: 77 raise e 78 return create_user(acceptDisclaimer=acceptDisclaimer, initBalance=initBalance, retryCnt=retryCnt+1) 79 finally: 80 kill_service(home, proc) 81 82 return username 83 84 def create_user_with_disclaimer(*_): 85 return create_user(acceptDisclaimer=True) 86 87 def create_user_with_balance(*_): 88 return create_user(initBalance=True) 89 90 if __name__ == '__main__': 91 if len(sys.argv) != 5: 92 print("Usage: `python3 airdrop-setup.py out.json X Y Z` where X is the number of users who have not accepted the disclaimer, Y is the number that have accepted the disclaimer, Z is the number that have a balance") 93 exit(2) 94 compile() 95 # The number of processes to use in parallel. This can be adjusted based off of your system. 96 p = Pool(16) 97 print("Creating users without the disclaimer...") 98 no_disclaimer = p.map(create_user, range(int(sys.argv[2]))) 99 print("Creating users with the disclaimer...") 100 accepted_disclaimer = p.map(create_user_with_disclaimer, range(int(sys.argv[3]))) 101 print("Creating users with a testnet balance...") 102 with_balance = p.map(create_user_with_balance, range(int(sys.argv[4]))) 103 print("Done! Writing to %s" % sys.argv[1]) 104 with open(sys.argv[1], 'w+') as f: 105 f.write(json.dumps({ 106 'no_disclaimer': no_disclaimer, 107 'accepted_disclaimer': accepted_disclaimer, 108 'with_balance': with_balance, 109 }))