Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
fix(wallet): increase fee limit on payment retries
Browse files Browse the repository at this point in the history
In the case the queryRoutes gives us an unplayable route we retry the
payment a couple of times. When doing the retries, increase the fee
limit to increase the chance of payment success.

fix 32952
  • Loading branch information
mrfelton committed Oct 22, 2019
1 parent 8e2276a commit 8c0569e
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 15 deletions.
3 changes: 2 additions & 1 deletion config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ module.exports = {
invoices: {
expire: 3600,
baseRetryDelay: 1000,
retryCount: 3, // Number of retries for pay invoice failure
retryCount: 2, // Number of retries for pay invoice failure
feeIncrementExponent: 1.1, // Exponent applied to fee limit on payment retry attempts
},

autopay: {
Expand Down
15 changes: 11 additions & 4 deletions renderer/components/Pay/Pay.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import get from 'lodash/get'
import { Box, Flex } from 'rebass'
import { animated, Keyframes, Transition } from 'react-spring/renderprops.cjs'
import { FormattedMessage, injectIntl, intlShape } from 'react-intl'
import { decodePayReq, getMinFee, getMaxFee, isOnchain, isLn } from '@zap/utils/crypto'
import {
decodePayReq,
getMinFee,
getMaxFee,
getMaxFeeInclusive,
isOnchain,
isLn,
} from '@zap/utils/crypto'
import { convert } from '@zap/utils/btc'
import {
Bar,
Expand Down Expand Up @@ -559,10 +566,10 @@ class Pay extends React.Component {

const formState = this.formApi.getState()
const { speed, payReq, isCoinSweep } = formState.values
let minFee, maxFee
let minFee, maxFeeInclusive
if (routes.length) {
minFee = getMinFee(routes)
maxFee = getMaxFee(routes)
maxFeeInclusive = getMaxFeeInclusive(routes)
}
const render = () => {
// convert entered amount to satoshis
Expand All @@ -584,7 +591,7 @@ class Pay extends React.Component {
return (
<PaySummaryLightning
amount={amount}
maxFee={maxFee}
maxFee={maxFeeInclusive}
minFee={minFee}
mt={-3}
payReq={payReq}
Expand Down
21 changes: 16 additions & 5 deletions renderer/reducers/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export const PAYMENT_SUCCESSFUL = 'PAYMENT_SUCCESSFUL'
export const PAYMENT_FAILED = 'PAYMENT_FAILED'
export const DECREASE_PAYMENT_RETRIES = 'DECREASE_PAYMENT_RETRIES'

const PAYMENT_STATUS_SENDING = 'sending'
const PAYMENT_STATUS_SUCCESSFUL = 'successful'
const PAYMENT_STATUS_FAILED = 'failed'

// ------------------------------------
// Helpers
// ------------------------------------
Expand Down Expand Up @@ -105,8 +109,14 @@ const getLastSendingEntry = (state, paymentRequest) =>
*/
const handleDecreaseRetries = (state, { paymentRequest }) => {
const paymentsSending = state.paymentsSending.map(payment => {
if (payment.paymentRequest === paymentRequest) {
return { ...payment, remainingRetries: payment.remainingRetries - 1 }
if (payment.paymentRequest === paymentRequest && payment.status === PAYMENT_STATUS_SENDING) {
return {
...payment,
remainingRetries: payment.remainingRetries - 1,
feeLimit: payment.feeLimit
? Math.ceil(payment.feeLimit * config.invoices.feeIncrementExponent)
: payment.feeLimit,
}
}
return payment
})
Expand Down Expand Up @@ -162,7 +172,7 @@ export const sendPayment = data => dispatch => {

const payment = {
...data,
status: 'sending',
status: PAYMENT_STATUS_SENDING,
isSending: true,
creation_date: Math.round(new Date() / 1000),
payment_hash: paymentHashTag.data,
Expand Down Expand Up @@ -308,6 +318,7 @@ export const paymentFailed = ({ payment_request, error }) => async (dispatch, ge
const paymentSending = getLastSendingEntry(state, payment_request)
// errors that trigger retry mechanism
const RETRIABLE_ERRORS = [
'payment attempt not completed before timeout', // ErrPaymentAttemptTimeout
'unable to find a path to destination', // ErrNoPathFound
'target not found', // ErrTargetNotInNetwork
]
Expand Down Expand Up @@ -365,7 +376,7 @@ const ACTION_HANDLERS = {
}
return {
...item,
status: 'successful',
status: PAYMENT_STATUS_SUCCESSFUL,
}
}),
}
Expand All @@ -379,7 +390,7 @@ const ACTION_HANDLERS = {
}
return {
...item,
status: 'failed',
status: PAYMENT_STATUS_FAILED,
error,
}
}),
Expand Down
31 changes: 26 additions & 5 deletions utils/crypto.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import get from 'lodash/get'
import config from 'config'
import range from 'lodash/range'
import { address } from 'bitcoinjs-lib'
import lightningRequestReq from 'bolt11'
import coininfo from 'coininfo'
Expand Down Expand Up @@ -172,9 +174,9 @@ export const getNodeAlias = (pubkey, nodes = []) => {
}

/**
* getMinFee - Given a list of routest, find the minimum fee.
* getMinFee - Given a list of routes, find the minimum fee.
*
* @param {*} routes List of routes
* @param {Array} routes List of routes
* @returns {number} minimum fee rounded up to the nearest satoshi
*/
export const getMinFee = (routes = []) => {
Expand All @@ -188,9 +190,9 @@ export const getMinFee = (routes = []) => {
}

/**
* getMaxFee - Given a list of routest, find the maximum fee.
* getMaxFee - Given a list of routes, find the maximum fee.
*
* @param {*} routes List of routes
* @param {Array} routes List of routes
* @returns {number} maximum fee
*/
export const getMaxFee = routes => {
Expand All @@ -203,10 +205,29 @@ export const getMaxFee = routes => {
return fee + 1
}

/**
* getMaxFee - Given a list of routes, find the maximum fee factoring in all possible payment retry attempts.
*
* @param {Array} routes List of routes
* @returns {number} maximum fee
*/
export const getMaxFeeInclusive = routes => {
if (!routes || !routes.length) {
return null
}

const {
invoices: { retryCount, feeIncrementExponent },
} = config

const fee = getMaxFee(routes)
return range(retryCount).reduce(max => Math.ceil(max * feeIncrementExponent), fee)
}

/**
* getFeeRange - Given a list of routest, find the maximum and maximum fee.
*
* @param {*} routes List of routes
* @param {Array} routes List of routes
* @returns {{min:number, max:number}} object with keys `min` and `max`
*/
export const getFeeRange = (routes = []) => ({
Expand Down

0 comments on commit 8c0569e

Please sign in to comment.