@GuardedBy("lock") private void receiveUpdatePaymentMessage(Protos.UpdatePayment msg, boolean sendAck) throws VerificationException, ValueOutOfRangeException, InsufficientMoneyException { log.info("Got a payment update"); Coin lastBestPayment = state.getBestValueToMe(); final Coin refundSize = Coin.valueOf(msg.getClientChangeValue()); boolean stillUsable = state.incrementPayment(refundSize, msg.getSignature().toByteArray()); Coin bestPaymentChange = state.getBestValueToMe().subtract(lastBestPayment); ListenableFuture<ByteString> ackInfoFuture = null; if (bestPaymentChange.signum() > 0) { ByteString info = (msg.hasInfo()) ? msg.getInfo() : null; ackInfoFuture = conn.paymentIncrease(bestPaymentChange, state.getBestValueToMe(), info); } if (sendAck) { final Protos.TwoWayChannelMessage.Builder ack = Protos.TwoWayChannelMessage.newBuilder(); ack.setType(Protos.TwoWayChannelMessage.MessageType.PAYMENT_ACK); if (ackInfoFuture == null) { conn.sendToClient(ack.build()); } else { Futures.addCallback( ackInfoFuture, new FutureCallback<ByteString>() { @Override public void onSuccess(@Nullable ByteString result) { if (result != null) ack.setPaymentAck(ack.getPaymentAckBuilder().setInfo(result)); conn.sendToClient(ack.build()); } @Override public void onFailure(Throwable t) { log.info("Failed retrieving paymentIncrease info future"); error( "Failed processing payment update", Protos.Error.ErrorCode.OTHER, CloseReason.UPDATE_PAYMENT_FAILED); } }); } } if (!stillUsable) { log.info("Channel is now fully exhausted, closing/initiating settlement"); settlePayment(CloseReason.CHANNEL_EXHAUSTED); } }