示例#1
0
 def __init__(self, lnddatadir):
     super().__init__('TIThread', 'TipperInvoiceThread')
     config = Config(read_timeout=65, retries=dict(max_attempts=10))
     self.sfn = boto3.client('stepfunctions',
                             config=config,
                             region_name='us-west-2')
     self.lnd = Client(lnddatadir)
    def run(self):
        self.logger.info('Starting Thread...')

        while True:
            try:
                self.logger.info('Listening for ' +
                                 binascii.hexlify(self.hash).decode())
                invoices = self.lnd.subscribeSingleInvoice(self.hash)

                for invoice in invoices:
                    self.logger.info(str(invoice))
                    self.logger.info({
                        'memo':
                        invoice.memo,
                        'r_hash':
                        binascii.hexlify(invoice.r_hash).decode()
                    })

                sleep(1)

            except grpc._channel._Rendezvous as e:
                self.logger.error('LND appears to be down...')
                self.lnd = Client()
                sleep(60)

            except Exception as e:
                self.logger.error('Error type: {}'.format(type(e)))
                self.logger.info('{}\n\n{}'.format(e, traceback.format_exc()))
                sleep(60)
    def __init__(self, preimage, hash):
        super().__init__()
        self.lnd = Client()
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.lamb = boto3.client('lambda',
                                 config=config,
                                 region_name='us-west-2')
        self.hash = hash
        self.preimage = preimage

        self.logger = logging.getLogger(name='SingleInvoiceListener')
        self.start()
示例#4
0
class TipperInvoiceThread(CommonThread):
    def __init__(self, lnddatadir):
        super().__init__('TIThread', 'TipperInvoiceThread')
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.sfn = boto3.client('stepfunctions',
                                config=config,
                                region_name='us-west-2')
        self.lnd = Client(lnddatadir)

    def tryRun(self):
        super().tryRun()
        # Make sure lnd is active before getting a task
        self.lnd.getInfo()

        response = self.sfn.get_activity_task(
            activityArn=
            'arn:aws:states:us-west-2:434623153115:activity:CdkStackgetTipperInvoiceActivity1238A05D',
            workerName='LNTipServer')

        if 'taskToken' in response and 'input' in response:
            try:
                token = response['taskToken']
                data = json.loads(response['input'])

                self.logger.info(data)

                if data['type'] == 'HodlTip':
                    preimage = os.urandom(32)
                    hash = hashlib.sha256(preimage)
                    data['preimage'] = binascii.hexlify(preimage).decode()
                    data['hash'] = hash.hexdigest()
                    data['tipperInvoice'] = self.lnd.requestHoldInvoice(
                        int(data['amount']), hash.digest(),
                        172800).payment_request
                    SingleInvoiceListener(preimage, hash.digest())
                else:
                    data['tipperInvoice'] = self.lnd.requestInvoice(
                        int(data['amount']), data['id']).payment_request

                self.sfn.send_task_success(
                    taskToken=token,
                    output=json.dumps(data),
                )
            except Exception as e:
                self.logger.error('Payment failed with {} {}'.format(
                    type(e), e))
                self.sfn.send_task_failure(taskToken=token,
                                           error="Failed",
                                           cause=str(e))

            with self.cond:
                self.cond.wait(5)
示例#5
0
    def __init__(self, datadir, lnddatadir):
        super().__init__('ISThread', 'InvoiceSubscriptionThread')
        self.lnd = Client(lnddatadir)
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.lamb = boto3.client('lambda',
                                 config=config,
                                 region_name='us-west-2')

        self.configPath = os.path.join(datadir, "config.json")

        if not os.path.isfile(self.configPath):
            self.logger.info(
                'Invoice subscription configuration not found, recreating with settleIndex 0'
            )
            json.dump({'settleIndex': 0}, open(self.configPath, 'w'))

        self.config = json.load(open(self.configPath))
示例#6
0
    def __init__(self, lnddatadir, btcdatadir):
        super().__init__('MetricThread', 'MetricThread')
        self.lnd = Client(lnddatadir)
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.cw = boto3.client('cloudwatch',
                               config=config,
                               region_name='us-west-2')

        with open(os.path.join(btcdatadir, 'bitcoin.conf'), 'r') as f:
            configLines = f.read().splitlines()

        self.btcConfig = {
            'rpcuser':
            next(filter(lambda x: x.startswith('rpcuser='******'rpcport':
            next(filter(lambda x: x.startswith('rpcport='), configLines),
                 None)[8:],
            'rpcpassword':
            next(filter(lambda x: x.startswith('rpcpassword='), configLines),
                 None)[12:],
        }
class SingleInvoiceListener(Thread):
    def __init__(self, preimage, hash):
        super().__init__()
        self.lnd = Client()
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.lamb = boto3.client('lambda',
                                 config=config,
                                 region_name='us-west-2')
        self.hash = hash
        self.preimage = preimage

        self.logger = logging.getLogger(name='SingleInvoiceListener')
        self.start()

    def run(self):
        self.logger.info('Starting Thread...')

        while True:
            try:
                self.logger.info('Listening for ' +
                                 binascii.hexlify(self.hash).decode())
                invoices = self.lnd.subscribeSingleInvoice(self.hash)

                for invoice in invoices:
                    self.logger.info(str(invoice))
                    self.logger.info({
                        'memo':
                        invoice.memo,
                        'r_hash':
                        binascii.hexlify(invoice.r_hash).decode()
                    })

                sleep(1)

            except grpc._channel._Rendezvous as e:
                self.logger.error('LND appears to be down...')
                self.lnd = Client()
                sleep(60)

            except Exception as e:
                self.logger.error('Error type: {}'.format(type(e)))
                self.logger.info('{}\n\n{}'.format(e, traceback.format_exc()))
                sleep(60)
示例#8
0
class MetricThread(CommonThread):
    def __init__(self, lnddatadir, btcdatadir):
        super().__init__('MetricThread', 'MetricThread')
        self.lnd = Client(lnddatadir)
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.cw = boto3.client('cloudwatch',
                               config=config,
                               region_name='us-west-2')

        with open(os.path.join(btcdatadir, 'bitcoin.conf'), 'r') as f:
            configLines = f.read().splitlines()

        self.btcConfig = {
            'rpcuser':
            next(filter(lambda x: x.startswith('rpcuser='******'rpcport':
            next(filter(lambda x: x.startswith('rpcport='), configLines),
                 None)[8:],
            'rpcpassword':
            next(filter(lambda x: x.startswith('rpcpassword='******'http://{}:{}@127.0.0.1:{}'.format(
                self.btcConfig['rpcuser'], self.btcConfig['rpcpassword'],
                self.btcConfig['rpcport']),
                                     json={'method': 'getnetworkinfo'})
            return response.json()['result']['networkactive']
        except:
            return False

    def isLndUp(self):
        try:
            self.lnd.getInfo()
            return True
        except:
            return False

    def tryRun(self):
        if datetime.utcnow().minute == 0:
            self.logger.info('Sending status...')

        btcUp = self.isBtcUp()
        lndUp = self.isLndUp()

        self.cw.put_metric_data(Namespace='LNTipBot',
                                MetricData=[{
                                    'MetricName': 'LndUp',
                                    'Timestamp': datetime.now(),
                                    'Value': 1 if lndUp else 0,
                                    'Unit': 'None',
                                    'StorageResolution': 60
                                }, {
                                    'MetricName': 'BtcUp',
                                    'Timestamp': datetime.now(),
                                    'Value': 1 if btcUp else 0,
                                    'Unit': 'None',
                                    'StorageResolution': 60
                                }])
        self.throttle()
示例#9
0
class PayInvoiceThread(CommonThread):
    def __init__(self, lnddatadir):
        super().__init__('PIThread', 'PayInvoiceThread')
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.sfn = boto3.client('stepfunctions',
                                config=config,
                                region_name='us-west-2')
        self.lnd = Client(lnddatadir)

    def handleTaskError(self, token, errorMessage):
        self.sfn.send_task_failure(taskToken=token,
                                   error="Failed",
                                   cause=str(errorMessage))

    def handleTask(self, token, data):
        self.logger.info('Thread started for payment: {}'.format(data))
        paymentResponse = None
        for x in range(1):
            try:
                paymentResponse = self.lnd.sendPayment(
                    data['invoice'], int(data['amount'] / 1000000))
                self.logger.info('paymentResponse: {}'.format(paymentResponse))
            except Exception as e:
                error = e
            else:
                if paymentResponse.payment_error:
                    error = paymentResponse.payment_error
                else:
                    data['paymentResponse'] = {
                        'payment_preimage':
                        paymentResponse.payment_preimage.hex(),
                        'payment_route': str(paymentResponse.payment_route),
                    }
                    self.logger.info('Payment succeeded with {}'.format(
                        paymentResponse.payment_preimage.hex()))
                    self.sfn.send_task_success(
                        taskToken=token,
                        output=json.dumps(data),
                    )
                    break
            self.logger.error('Payment failed with {} {}'.format(
                type(error), error))
        else:
            self.handleTaskError(token, error)

    def tryRun(self):
        # Make sure lnd is active before getting a task
        self.lnd.getInfo()

        response = self.sfn.get_activity_task(
            activityArn=
            'arn:aws:states:us-west-2:434623153115:activity:CdkStackpayInvoiceActivityB30C5FBC',
            workerName='LNTipServer')

        if 'taskToken' in response and 'input' in response:
            token = response['taskToken']
            data = json.loads(response['input'])

            # TODO: join all threads before exiting this one
            Thread(target=self.handleTask, args=(token, data)).start()

            with self.cond:
                self.cond.wait(10)
示例#10
0
class InvoiceSubscriptionThread(CommonThread):
    def __init__(self, datadir, lnddatadir):
        super().__init__('ISThread', 'InvoiceSubscriptionThread')
        self.lnd = Client(lnddatadir)
        config = Config(read_timeout=65, retries=dict(max_attempts=10))
        self.lamb = boto3.client('lambda',
                                 config=config,
                                 region_name='us-west-2')

        self.configPath = os.path.join(datadir, "config.json")

        if not os.path.isfile(self.configPath):
            self.logger.info(
                'Invoice subscription configuration not found, recreating with settleIndex 0'
            )
            json.dump({'settleIndex': 0}, open(self.configPath, 'w'))

        self.config = json.load(open(self.configPath))

    def shutdown(self):
        super().shutdown()
        self.lnd.channel.close()

    def updateSettleIndex(self, index):
        self.config['settleIndex'] = index
        json.dump(self.config, open(self.configPath, 'w'))

    def tryRun(self):
        try:
            invoices = self.lnd.subscribeInvoices(
                settleIndex=self.config['settleIndex'])

            for invoice in invoices:
                if invoice.settle_index > 0:
                    payload = {
                        'memo': invoice.memo,
                        'value': str(invoice.value),
                        'amt_paid': str(invoice.amt_paid * 1000)
                    }
                    self.logger.info(payload)
                    response = self.lamb.invoke(
                        FunctionName=
                        'arn:aws:lambda:us-west-2:434623153115:function:CdkStack-settledInvoiceHandler38092B08-4MojK9KjXaDx',
                        InvocationType='RequestResponse',
                        Payload=json.dumps(payload),
                    )
                    self.updateSettleIndex(invoice.settle_index)

                    if 'FunctionError' in response:
                        self.logger.error(response)
                else:
                    self.logger.info({
                        'memo': invoice.memo,
                        'r_hash': invoice.r_hash
                    })
        except grpc._channel._Rendezvous as e:
            if e.code() == grpc.StatusCode.CANCELLED:
                self.logger.info('Subscription interrupted')
                return

        with self.cond:
            self.cond.wait(1)