def provider_handle_pay_request(self, nexus, bolt11, request_uuid): msats = Bolt11.get_msats(bolt11) if not msats: return "no amount specified in bolt11" payment_hash = Bolt11.get_payment_hash(bolt11) shared_seed = nexus.get_shared_seed() liability_account = self.liabilities.lookup_by_seed(shared_seed) spendable_msats = (liability_account.get_msatoshis() - liability_account.get_pending_msatoshis()) if msats > spendable_msats: return "insufficent balance to pay" # decide which of our asset accounts will pay the bolt11 asset_nexus_uuid = self.asset_pool.select_paying_nexus_uuid(msats) if not asset_nexus_uuid: return "no account capable of receiving" liability_account.add_paying(payment_hash, bolt11) self.liabilities.reindex_account(liability_account) our_request_uuid = self.consumer_stack.request_pay( asset_nexus_uuid, bolt11) self.pay_requests[our_request_uuid] = { 'liability_nexus_uuid': nexus.uuid, 'liability_request_uuid': request_uuid, 'liability_account_uuid': liability_account.uuid, 'bolt11': bolt11, 'asset_nexus_uuid': asset_nexus_uuid }
def _handle_paying_preimage(self, nexus, preimage, request_reference_uuid): payment_hash = Bolt11.preimage_to_payment_hash(preimage) logging.info("preimage %s payment_hash %s" % (preimage, payment_hash)) liability_accounts = list( self.liabilities.lookup_by_paying_payment_hash(payment_hash)) assert len(liability_accounts) <= 1, "can't handle collision yet" if len(liability_accounts) == 0: logging.error("got preimage not associated to liability?") return liability_account = liability_accounts[0] shared_seeds = liability_account.get_all_shared_seeds() msats_sent = [ Bolt11.get_msats(bolt11) for ph, bolt11 in liability_account.iter_paying() if ph == payment_hash ][0] liability_account.remove_paying(payment_hash) liability_account.subtract_wad(Wad.bitcoin(msats_sent)) self.liabilities.reindex_account(liability_account) if request_reference_uuid in self.pay_requests: info = self.pay_requests.pop(request_reference_uuid) rrid = info['liability_request_uuid'] else: rrid = None self.provider_stack.notify_preimage(shared_seeds, preimage, rrid)
def node_received_payment_cb(self, preimage, msats): received_wad = Wad.bitcoin(msats) logging.info("node received payment: %s %s" % (preimage, received_wad)) payment_hash = Bolt11.preimage_to_payment_hash(preimage) # find accounts with this payment_hash accounts = self.directory.lookup_by_payment_hash(payment_hash) if len(accounts) > 1: logging.error("can't deal with more than one account with " "a preimage collision yet") # TODO deal with this somehow - feed preimage back into lightning # node to claim any pending htlcs? return if len(accounts) == 0: logging.error("incoming payment not known") return account = list(accounts)[0] shared_seeds = account.get_all_shared_seeds() account.remove_pending(payment_hash) account.add_wad(received_wad) self.provider_stack.notify_preimage(shared_seeds, preimage, None) for ss in shared_seeds: account.session_preimage_notified(ss, preimage, True, msats)
def handle_invoice_request(self, nexus, msats, request_uuid): shared_seed = nexus.get_shared_seed() account = self.directory.lookup_by_seed(shared_seed) assert account is not None, "shared seed not from known account?" shared_seeds = account.get_all_shared_seeds() account.session_invoice_requested(shared_seed, msats) wad = account.get_wad() cap = account.get_cap() if cap['msats'] != 0 and (msats + wad['msats'] > cap['msats']): # TODO 0 track pending incoming? err = "account cap exceeded" account.session_error_notified(shared_seed, err) self.provider_error(shared_seeds, err, request_uuid) return bolt11, err = self.lightning.get_invoice(msats) if err: account.session_error_notified(shared_seed, err) self.provider_error(shared_seeds, err, request_uuid) return payment_hash = Bolt11.get_payment_hash(bolt11) account.add_pending(payment_hash, bolt11) for ss in shared_seeds: account.session_invoice_notified(shared_seed, bolt11) self.directory.reindex_account(account) self.provider_stack.notify_invoice(shared_seeds, bolt11, request_uuid)
def consumer_on_error(self, nexus, error_msg, request_reference_uuid): print("got error: %s" % error_msg) if request_reference_uuid in self.invoice_requests.keys(): r = self.invoice_requests.pop(request_reference_uuid) account_uuid = r['liability_account_uuid'] liability_account = self.liabilities.lookup_by_uuid(account_uuid) shared_seeds = liability_account.get_all_shared_seeds() liability_rrid = r['liability_request_uuid'] self.provider_stack.notify_error(shared_seeds, error_msg, liability_rrid) print("notified provider of invoice error: %s" % error_msg) elif request_reference_uuid in self.pay_requests.keys(): r = self.pay_requests.pop(request_reference_uuid) account_uuid = r['liability_account_uuid'] liability_account = self.liabilities.lookup_by_uuid(account_uuid) shared_seeds = liability_account.get_all_shared_seeds() liability_rrid = r['liability_request_uuid'] self.provider_stack.notify_error(shared_seeds, error_msg, liability_rrid) bolt11 = r['bolt11'] payment_hash = Bolt11.get_payment_hash(bolt11) liability.account.remove_paying(payment_hash) print("notified provider of pay error: %s" % error_msg) else: print("unknown error notification: %s" % error_msg)
def prune_expired_pending(self): for pending, bolt11 in list(self.iter_pending()): info = Bolt11.to_dict(bolt11) expire_timestamp = info['created_at'] + info['expiry'] #print("%s expire: %f" % (expire_timestamp, # expire_timestamp - time.time())) if time.time() > expire_timestamp: del self.db['pending'][pending]
def get_invoice(self, msat_amount): logging.info("getting invoice: %smsats" % msat_amount) sat_amount = int(round(msat_amount / 1000.0)) i = self.lnd_client.add_invoice("", sat_amount) logging.info("got: %s" % i) payment_hash = Bolt11.to_dict(i.payment_request)['payment_hash'] self.pending_payment_hashes.add(payment_hash) # TODO persist and also periodically prune pending hashes. return i.payment_request
def terminus_handle_invoice_request(self, shared_seed, msats): # TODO - this should be a request-> callback to allow for an # asynchronous call to the daemon. account = self.directory.lookup_by_seed(shared_seed) assert account is not None, "shared seed not from known account?" # TODO handle failure bolt11 = self.lightning.get_invoice(msats) payment_hash = Bolt11.get_payment_hash(bolt11) account.add_pending(payment_hash, bolt11) self.directory.reindex_account(account) return {'bolt11': bolt11}
def consumer_on_invoice(self, nexus, bolt11, request_reference_uuid): if request_reference_uuid not in self.invoice_requests: logging.error("got bolt11 not requested? %s" % request_reference_uuid) return request_info = self.invoice_requests.pop(request_reference_uuid) liability_nexus_uuid = request_info['liability_nexus_uuid'] liability_request_uuid = request_info['liability_request_uuid'] liability_account_uuid = request_info['liability_account_uuid'] asset_nexus_uuid = request_info['asset_nexus_uuid'] msats_requested = request_info['msats_requested'] liability_account = self.liabilities.lookup_by_uuid( liability_account_uuid) if asset_nexus_uuid != nexus.uuid: logging.info("got bolt11 from different nexus than requested?") if not liability_account: logging.error("liability account removed?") return bolt11_msats = Bolt11.get_msats(bolt11) if bolt11_msats and bolt11_msats != msats_requested: logging.error("got wrong msats amount?") return bolt11_payment_hash = Bolt11.get_payment_hash(bolt11) liability_account.add_pending(bolt11_payment_hash, bolt11) self.liabilities.reindex_account(liability_account) shared_seeds = liability_account.get_all_shared_seeds() self.provider_stack.notify_invoice(shared_seeds, bolt11, liability_request_uuid)
def handle_pay_request(self, nexus, bolt11, request_uuid): shared_seed = nexus.get_shared_seed() account = self.directory.lookup_by_seed(shared_seed) assert account is not None, "shared seed not from known account?" shared_seeds = account.get_all_shared_seeds() msats = Bolt11.get_msats(bolt11) if (msats is None): err = "bolt11 does not specify amount", account.session_error_notified(shared_seed, err) self.provider_error(shared_seeds, err, request_uuid) return wad = account.get_wad() if msats > wad['msats']: # TODO - estimate routing fees? err = "insufficent account balance" account.session_error_notified(shared_seed, err) self.provider_error(shared_seeds, err, request_uuid) return account.session_pay_requested(shared_seed, bolt11) preimage, paid_msats, err = self.lightning.pay_invoice( bolt11, request_uuid) if err: account.session_error_notified(shared_seed, err) self.provider_error(shared_seeds, err, request_uuid) return paid_wad = Wad.bitcoin(paid_msats) if (paid_msats > wad['msats']): # routing fees could have exceeded balance... need to figure # out the best way to deal with this paid_msats = wad['msats'] account.subtract_wad(paid_wad) # TODO new pay preimage message to propagate fees self.provider_stack.notify_preimage(shared_seeds, preimage, request_uuid) for ss in shared_seeds: account.session_preimage_notified(ss, preimage, False, paid_msats)
def terminus_handle_pay_request(self, shared_seed, bolt11): # TODO - this should be a request-> callback to allow for an # asynchronous call to the daemon. account = self.directory.lookup_by_seed(shared_seed) assert account is not None, "shared seed not from known account?" msats = Bolt11.get_msats(bolt11) wad = account.get_wad() if msats > wad['msats']: return # msatoshi balance exceeded # TODO handle failure preimage, paid_msats = self.lightning.pay_invoice(bolt11) paid_wad = Wad.bitcoin(paid_msats) account.subtract_wad(paid_wad) shared_seeds = account.get_all_shared_seeds() # TODO pay preimage self.terminus_stack.notify_preimage(shared_seeds, preimage)
def session_pay_requested(self, shared_seed, bolt11): msats = Bolt11.get_msats(bolt11) wad = Wad.bitcoin(msats) entry = SocketSessionReceipt.pay_request_entry(bolt11, wad) self.db.add_receipt_entry(shared_seed, entry)
def get_pending_msatoshis(self): msats = 0 for _, bolt11 in self.get_pending(): msats += Bolt11.get_msats(bolt11) return msats