rgb-cln/contrib/giantnode.py

184 lines
6.0 KiB
Python
Executable File

#! /usr/bin/env python3
# Script to create huge numbers of forwards/payments/etc for regtest nodes.
# To initialize, use contrib/startup_regtest.sh:
# $ . contrib/startup_regtest.sh
# $ start_ln 3
# $ fund_nodes
import argparse
import random
import string
import sys
import time
import multiprocessing
from pyln.client import LightningRpc, RpcError
parser = argparse.ArgumentParser(
description='Flood three funded startup_regtest nodes'
)
parser.add_argument('--fail-forward', action="store_true",
help='Create failed forward attempts',
default=False)
parser.add_argument('--fail-pay', action="store_true",
help='Allow some failed pay attempts (faster!)',
default=False)
parser.add_argument('--inv-runners', type=int,
help='How many invoice-generation processes to run at once',
default=1)
parser.add_argument('--pay-runners', type=int,
help='How many invoice-paying processes to run at once',
default=8)
parser.add_argument('--check-runners', type=int,
help='How many pay-checking processes to run at once',
default=1)
parser.add_argument('--allow-bookkeeper', action="store_true",
help="Don't stop if bookkeeper is running",
default=False)
parser.add_argument('num', type=int, nargs='?',
help='number to attempt',
default=1000000)
args = parser.parse_args()
nodes = (LightningRpc('/tmp/l1-regtest/regtest/lightning-rpc'),
LightningRpc('/tmp/l2-regtest/regtest/lightning-rpc'),
LightningRpc('/tmp/l3-regtest/regtest/lightning-rpc'))
nodes = (LightningRpc('/tmp/l1-regtest/regtest/lightning-rpc'),
LightningRpc('/tmp/l2-regtest/regtest/lightning-rpc'),
LightningRpc('/tmp/l3-regtest/regtest/lightning-rpc'))
# Convenient aliases
l1 = nodes[0]
l2 = nodes[1]
l3 = nodes[2]
if not args.allow_bookkeeper:
if any([p['name'].endswith('bookkeeper') for p in l1.plugin_list()['plugins']]):
print("""
Bookkeeper is running on l1, will slow things down! Run this:
echo 'disable-plugin=bookkeeper' >> /tmp/l1-regtest/regtest/config
echo 'disable-plugin=bookkeeper' >> /tmp/l2-regtest/regtest/config
echo 'disable-plugin=bookkeeper' >> /tmp/l3-regtest/regtest/config
stop_ln
start_ln 3
""")
sys.exit(1)
route = l1.getroute(l3.getinfo()['id'], 1, 1)['route']
if args.fail_forward:
route[1]['channel'] = '1x1x1'
def get_invs(inv_q, num, report_q):
"""Runners feed invoices into the queue"""
prefix = ''.join(random.choice(string.ascii_lowercase) for _ in range(10))
for i in range(num):
inv_q.put(l3.invoice(amount_msat=1,
label='{}-{}'.format(prefix, i),
description='giantnode'))
report_q.put('i')
def send_pay(inv_q, num, done_q, report_q):
"""Runner to fetch invoices from queue and send"""
for i in range(num):
inv = inv_q.get()
l1.sendpay(route,
inv['payment_hash'],
payment_secret=inv['payment_secret'])
report_q.put('p')
done_q.put(inv)
def checker(num, inv_q, done_q, report_q):
"""Runner which checks that invs didn't fail (TOO_MANY_HTLCS!), and requeues if they did!"""
for i in range(num):
inv = done_q.get()
try:
res = l1.waitsendpay(inv['payment_hash'])
except RpcError:
report_q.put('f')
if not args.fail_pay and not args.fail_forward:
inv_q.put(inv)
continue
assert res['status'] == 'complete'
report_q.put('w')
inv_q = multiprocessing.Queue()
done_q = multiprocessing.Queue()
report_q = multiprocessing.Queue()
# In case it doesn't divide
extra_prod = (args.num % args.inv_runners,) + (0,) * (args.inv_runners - 1)
inv_producers = [multiprocessing.Process(target=get_invs, args=(inv_q, args.num // args.inv_runners + extra_prod[i], report_q)) for i in range(args.inv_runners)]
extra_cons = (args.num % args.pay_runners,) + (0,) * (args.pay_runners - 1)
inv_consumers = [multiprocessing.Process(target=send_pay, args=(inv_q, args.num // args.pay_runners + extra_cons[i], done_q, report_q)) for i in range(args.pay_runners)]
extra_checks = (args.num % args.check_runners,) + (0,) * (args.check_runners - 1)
checkers = [multiprocessing.Process(target=checker, args=(args.num // args.check_runners + extra_checks[i], inv_q, done_q, report_q)) for i in range(args.check_runners)]
prev = start = time.time()
for i in inv_producers + inv_consumers + checkers:
i.start()
num_total = 0
num_successes = 0
num_invs = 0
num_pays = 0
num_retries = 0
prev_total = 0
def timefmt(number, time):
if time == 0:
return '?'
seconds = number / time
minutes = seconds / 60
hours = minutes / 60
if hours > 2:
return '{} hours'.format(int(hours))
if minutes > 2:
return '{} minutes'.format(int(minutes))
return '{} seconds'.format(int(seconds))
while num_total < args.num:
letter = report_q.get()
if letter == 'i':
num_invs += 1
elif letter == 'p':
num_pays += 1
elif letter == 'f':
num_retries += 1
elif letter == 'w':
num_successes += 1
else:
assert False
if args.fail_pay or args.fail_forward:
num_total = num_pays
else:
num_total = num_successes
now = time.time()
if now > prev + 10:
current_rate = (num_total - prev_total) / (now - prev)
prev = now
prev_total = num_total
total_rate = num_total / (now - start)
print("{}/{} complete {}/sec ({} invs, {} pays, {} retries) in {} seconds. {}-{} remaining."
.format(num_total, args.num, format(current_rate, ".2f"),
num_invs, num_pays, num_retries, int(now - start),
timefmt(args.num - num_total, total_rate),
timefmt(args.num - num_total, current_rate)))
for i in inv_producers + inv_consumers + checkers:
i.join()
print("done")