public void completeTx(SendRequest req) throws InsufficientMoneyException, CompletionException {
        lock.lock();
        try {
            checkArgument(!req.completed, () ->
                    "given SendRequest has already been completed");

            log.info("Completing send tx with {} outputs totalling {} and a fee of {}/vkB", req.tx.getOutputs().size(),
                    req.tx.getOutputSum().toFriendlyString(), req.feePerKb.toFriendlyString());

            // Calculate a list of ALL potential candidates for spending and then ask a coin selector to provide us
            // with the actual outputs that'll be used to gather the required amount of value. In this way, users
            // can customize coin selection policies. The call below will ignore immature coinbases and outputs
            // we don't have the keys for.
            List<TransactionOutput> prelimCandidates = calculateAllSpendCandidates(true, req.missingSigsMode == MissingSigsMode.THROW);

            // Connect (add a value amount) unconnected inputs
            List<TransactionInput> inputs = connectInputs(prelimCandidates, req.tx.getInputs());
            req.tx.clearInputs();
            inputs.forEach(req.tx::addInput);

            // Warn if there are remaining unconnected inputs whose value we do not know
            // TODO: Consider throwing if there are inputs that we don't have a value for
            if (req.tx.getInputs().stream()
                    .map(TransactionInput::getValue)
                    .anyMatch(Objects::isNull))
                log.warn("SendRequest transaction already has inputs but we don't know how much they are worth - they will be added to fee.");

            // If any inputs have already been added, we don't need to get their value from wallet
            Coin totalInput = req.tx.getInputSum();
            // Calculate the amount of value we need to import.
            Coin valueNeeded = req.tx.getOutputSum().subtract(totalInput);

            // Enforce the OP_RETURN limit
            if (req.tx.getOutputs().stream()
                    .filter(o -> ScriptPattern.isOpReturn(o.getScriptPubKey()))
                    .count() > 1) // Only 1 OP_RETURN per transaction allowed.
                throw new MultipleOpReturnRequested();

            // Check for dusty sends
            if (req.ensureMinRequiredFee && !req.emptyWallet) { // Min fee checking is handled later for emptyWallet.
                if (req.tx.getOutputs().stream().anyMatch(TransactionOutput::isDust))
                    throw new DustySendRequested();
            }

            // Filter out candidates that are already included in the transaction inputs
            List<TransactionOutput> candidates = prelimCandidates.stream()
                    .filter(output -> alreadyIncluded(req.tx.getInputs(), output))
                    .collect(StreamUtils.toUnmodifiableList());

            CoinSelection bestCoinSelection;
            TransactionOutput bestChangeOutput = null;
            List<Coin> updatedOutputValues = null;
            if (!req.emptyWallet) {
                // This can throw InsufficientMoneyException.
                FeeCalculation feeCalculation = calculateFee(req, valueNeeded, req.ensureMinRequiredFee, candidates);
                bestCoinSelection = feeCalculation.bestCoinSelection;
                bestChangeOutput = feeCalculation.bestChangeOutput;
                updatedOutputValues = feeCalculation.updatedOutputValues;
            } else {
                // We're being asked to empty the wallet. What this means is ensuring "tx" has only a single output
                // of the total value we can currently spend as determined by the selector, and then subtracting the fee.
                checkState(req.tx.getOutputs().size() == 1, () ->
                        "empty wallet TX must have a single output only");
                CoinSelector selector = req.coinSelector == null ? coinSelector : req.coinSelector;
                bestCoinSelection = selector.select((Coin) network.maxMoney(), candidates);
                candidates = null;  // Selector took ownership and might have changed candidates. Don't access again.
                req.tx.getOutput(0).setValue(bestCoinSelection.totalValue());
                log.info("  emptying {}", bestCoinSelection.totalValue().toFriendlyString());
            }

            bestCoinSelection.outputs()
                    .forEach(req.tx::addInput);

            if (req.emptyWallet) {
                if (!adjustOutputDownwardsForFee(req.tx, bestCoinSelection, req.feePerKb, req.ensureMinRequiredFee))
                    throw new CouldNotAdjustDownwards();
            }

            if (updatedOutputValues != null) {
                for (int i = 0; i < updatedOutputValues.size(); i++) {
                    req.tx.getOutput(i).setValue(updatedOutputValues.get(i));
                }
            }

            if (bestChangeOutput != null) {
                req.tx.addOutput(bestChangeOutput);
                log.info("  with {} change", bestChangeOutput.getValue().toFriendlyString());
            }

            // Now shuffle the outputs to obfuscate which is the change.
            if (req.shuffleOutputs)
                req.tx.shuffleOutputs();

            // Now sign the inputs, thus proving that we are entitled to redeem the connected outputs.
            if (req.signInputs)
                signTransaction(req);

            // Check size.
            final int size = req.tx.messageSize();
            if (size > Transaction.MAX_STANDARD_TX_SIZE)
                throw new ExceededMaxTransactionSize();

            // Label the transaction as being self created. We can use this later to spend its change output even before
            // the transaction is confirmed. We deliberately won't bother notifying listeners here as there's not much
            // point - the user isn't interested in a confidence transition they made themselves.
            getConfidence(req.tx).setSource(TransactionConfidence.Source.SELF);
            // Label the transaction as being a user requested payment. This can be used to render GUI wallet
            // transaction lists more appropriately, especially when the wallet starts to generate transactions itself
            // for internal purposes.
            req.tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
            // Record the exchange rate that was valid when the transaction was completed.
            req.tx.setExchangeRate(req.exchangeRate);
            req.tx.setMemo(req.memo);
            req.completed = true;
            log.info("  completed: {}", req.tx);
        } finally {
            lock.unlock();
        }
    }
@Test
    public void sendRequestExchangeRate() throws Exception {
        receiveATransaction(wallet, myAddress);
        SendRequest sendRequest = SendRequest.to(myAddress, Coin.COIN);
        sendRequest.exchangeRate = new ExchangeRate(Fiat.parseFiat("EUR", "500"));
        wallet.completeTx(sendRequest);
        assertEquals(sendRequest.exchangeRate, sendRequest.tx.getExchangeRate());
    }