Пример #1
0
    def test_handleHTLCAccepted_failed(self):
        self.interface.handleRequest(
            6, 'htlc_accepted', {
                'onion': {
                    'payload': '12fe424c34500cf1f2f3f4f5f6f7f8c1c2c3c4',
                },
                'htlc': {
                    'amount': '1234msat',
                    'cltv_expiry': 42,
                    'payment_hash': 'cafecafe',
                },
            })
        self.assertEqual(self.output.buffer, b'')
        self.client.handleIncomingMessage.assert_called_once_with(
            messages.LNIncoming(
                paymentHash=b'\xca\xfe\xca\xfe',
                cryptoAmount=1234,
                CLTVExpiryDelta=42,
                fiatAmount=0xf1f2f3f4f5f6f7f8,
                offerID=0xc1c2c3c4,
            ))

        self.interface.handleMessage(
            messages.LNFail(paymentHash=b'\xca\xfe\xca\xfe', ))
        self.checkJSONOutput({
            'jsonrpc': '2.0',
            'id': 6,
            'result': {
                'result': 'fail',
            },
        })
Пример #2
0
    async def cancelTransactionOnLightning(self) -> None:
        assert isinstance(self.order, BuyOrder)
        assert isinstance(self.transaction, BuyTransaction)

        self.client.handleOutgoingMessage(
            messages.LNFail(paymentHash=self.transaction.paymentHash, ))
        self.transaction = None

        logging.info('Buy transaction is canceled')

        #TODO: is this really needed?
        await self.updateOrderAfterTransaction()
Пример #3
0
 def handleLNIncoming(self, message: messages.LNIncoming) -> None:
     localID = message.offerID  #type: int
     try:
         self.orderTasks[localID].setCallResult(message)
     except:
         logging.exception(
             'Exception on trying to handle an incoming Lightning transaction for local ID '
             + str(localID))
         logging.error(
             'Apparently we can\'t handle the transaction right now, so we are refusing the incoming transaction.'
         )
         self.client.handleOutgoingMessage(
             messages.LNFail(paymentHash=message.paymentHash))
Пример #4
0
    async def test_buyer_repeatCanceledTransaction(self):
        orderID = ordertask.BuyOrder.create(
            self.storage,
            190000,  #mCent / BTC = 1.9 EUR/BTC
            123400000  #mCent    = 1234 EUR
        )
        order = ordertask.BuyOrder(self.storage, orderID, 'buyerAddress')
        order.remoteOfferID = 6
        order.setAmount = Mock()

        self.storage.buyTransactions = \
        {
        41:
         {
         'ID': 41,
         'buyOrder': orderID,
         'status': 5, #canceled
         'fiatAmount': 100000000,
         'cryptoAmount': 200000000,
         'paymentHash': b'foo',
         },
        42:
         {
         'ID': 42,
         'buyOrder': orderID,
         'status': 0,
         'fiatAmount': 300000000,
         'cryptoAmount': 600000000,
         'paymentHash': b'foo2',
         }
        }

        task = ordertask.OrderTask(self.client, self.storage, order)
        task.startup()

        await asyncio.sleep(0.1)

        task.setCallResult(
            messages.LNIncoming(
                offerID=42,
                CLTVExpiryDelta=0,
                fiatAmount=100000000,
                cryptoAmount=200000000,
                paymentHash=b'foo',
            ))

        msg = await self.outgoingMessages.get()
        self.assertEqual(msg, messages.LNFail(paymentHash=b'foo', ))

        await self.shutdownOrderTask(task)
Пример #5
0
    async def test_failedBuyTransaction(self):
        orderID = ordertask.BuyOrder.create(
            self.storage,
            190000,  #mCent / BTC = 1.9 EUR/BTC
            123400000  #mCent    = 1234 EUR
        )
        order = ordertask.BuyOrder(self.storage, orderID, 'buyerAddress')
        order.remoteOfferID = 6
        order.setAmount = Mock()

        self.storage.buyTransactions = \
        {
        41:
         {
         'ID': 41,
         'buyOrder': orderID,
         'status': 0,
         'fiatAmount': 100000000,
         'cryptoAmount': 200000000,
         'paymentHash': b'foo',
         }
        }

        task = ordertask.OrderTask(self.client, self.storage, order)
        task.startup()

        await asyncio.sleep(0.1)

        task.setCallResult(
            messages.LNIncoming(
                offerID=42,
                CLTVExpiryDelta=0,
                fiatAmount=100000000,
                cryptoAmount=200000000,
                paymentHash=b'foo',
            ))

        msg = await self.outgoingMessages.get()
        self.assertEqual(msg, messages.BL4PSend(
         localOrderID=42,

         amount = 100000000,
         paymentHash = b'foo',
         max_locked_timeout_delta_s = 3600*24*14,
         selfReport = \
          {
          'paymentHash'         : '666f6f', #foo in hex
          'offerID'             : str(orderID),
          'receiverCryptoAmount': '0.00200000000',
          'cryptoCurrency'      : 'btc',
          },
         ))
        task.setCallResult(messages.BL4PError(request=None, ))

        #LN transaction gets canceled
        msg = await self.outgoingMessages.get()
        self.assertEqual(msg, messages.LNFail(paymentHash=b'foo', ))

        order.setAmount.assert_called_once_with(223400000)
        self.assertEqual(
            self.storage.buyTransactions, {
                41: {
                    'ID': 41,
                    'status': ordertask.TX_STATUS_CANCELED,
                    'buyOrder': orderID,
                    'fiatAmount': 100000000,
                    'cryptoAmount': 200000000,
                    'paymentHash': b'foo',
                }
            })
        self.assertEqual(task.transaction, None)

        #Old offer gets removed
        msg = await self.outgoingMessages.get()
        self.assertTrue(isinstance(msg, messages.BL4PRemoveOffer))
        task.setCallResult(messages.BL4PRemoveOfferResult(request=None, ))

        #New offer gets added
        msg = await self.outgoingMessages.get()
        self.assertTrue(isinstance(msg, messages.BL4PAddOffer))
        task.setCallResult(messages.BL4PAddOfferResult(
            request=None,
            ID=6,
        ))

        #Continues to next iteration:
        await asyncio.sleep(0.1)

        await self.shutdownOrderTask(task)
Пример #6
0
 def test_sendFail_withoutCall(self):
     #This must be a NOP:
     self.interface.handleMessage(
         messages.LNFail(paymentHash=b'\xca\xfe\xca\xfe', ))
     self.assertEqual(self.output.buffer, b'')
Пример #7
0
    async def waitForIncomingTransaction(self) -> None:
        assert isinstance(self.order, BuyOrder)

        message = cast(messages.LNIncoming, await self.waitForIncomingMessage(
            messages.LNIncoming))  #type: messages.LNIncoming

        logging.info('Received incoming Lightning transaction')
        #TODO: log transaction characteristics
        #TODO: maybe refuse tx if we're not connected to BL4P

        #Check if this is a new notification for an already ongoing tx.
        cursor = self.storage.execute(
            'SELECT ID from buyTransactions WHERE paymentHash = ?',
            [message.paymentHash])  #type: Cursor
        transactions = [BuyTransaction(self.storage, row[0]) for row in cursor]
        if transactions:
            logging.info(
                'A transaction with this payment hash already exists in our database'
            )
            self.transaction = transactions[0]
            if self.transaction.paymentPreimage is not None:
                logging.info(
                    'We already have the preimage, so we claim the Lightning funds'
                )
                await self.finishTransactionOnLightning()
                return

            #TODO (bug 19): maybe check if the incoming tx equals this tx?

            if self.transaction.status == TX_STATUS_CANCELED:
                logging.info(
                    'The transaction was canceled, so we cancel the Lightning tx'
                )
                await self.cancelTransactionOnLightning()
            elif self.transaction.status == TX_STATUS_INITIAL:
                logging.info(
                    'The transaction was not finished yet, so try again to finish it'
                )
                await self.sendFundsOnBL4P()
            else:
                #TODO: properly report database inconsistency error
                raise Exception(
                    'Invalid transaction status value in unfinished transaction'
                )

            return

        #Check if lntx conforms to our order:
        counterOffer = Offer(
            #These are equivalent to our order, except for max_amount.
            #max_amount will be overwritten - see below
            bid=copy.deepcopy(self.order.ask),
            ask=copy.deepcopy(self.order.bid),

            #dummy values:
            address='',
            ID=0,

            #Don't specify sender_timeout: we can just try if we're still within the timeout
            #Don't specify locked_timeout: it is unknown to us; we will inform BL4P about our maximum
        )  #type: offer.Offer
        counterOffer.bid.max_amount = message.cryptoAmount
        counterOffer.ask.max_amount = message.fiatAmount

        try:
            counterOffer.verifyMatches(self.order)
        except offer.MismatchError as error:
            logging.info(
                'Received transaction did not match our order - refusing it.')
            logging.info('The mismatch is: ' + str(error))
            self.client.handleOutgoingMessage(
                messages.LNFail(paymentHash=message.paymentHash, ))
            return

        #TODO: (bug 5) check max per-tx amount
        #TODO: (bug 5) check that we still have sufficient time
        #according to our cltv_expiry_delta

        #Check if remaining order size is sufficient:
        assert message.fiatAmount <= self.order.amount

        buyTransactionID = BuyTransaction.create(
            self.storage,
            buyOrder=self.order.ID,
            fiatAmount=message.fiatAmount,
            cryptoAmount=message.cryptoAmount,
            paymentHash=message.paymentHash,
        )  #type: int
        self.order.setAmount(self.order.amount - message.fiatAmount)
        self.transaction = BuyTransaction(self.storage, buyTransactionID)

        await self.sendFundsOnBL4P()