@Test
public void recipientPaysFees() throws Exception {

    sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN);
    // Simplest recipientPaysFees use case
    Coin valueToSend = CENT.divide(2);
    SendRequest request = SendRequest.to(OTHER_ADDRESS, valueToSend);
    request.feePerKb = Transaction.DEFAULT_TX_FEE;
    request.ensureMinRequiredFee = true;
    request.recipientsPayFees = true;
    request.shuffleOutputs = false;
    wallet.completeTx(request);
    // Hardcoded tx length because actual length may vary depending on actual signature length
    Coin fee = request.feePerKb.multiply(227).divide(1000);
    assertEquals(fee, request.tx.getFee());
    Transaction spend = request.tx;
    assertEquals(2, spend.getOutputs().size());
    assertEquals(valueToSend.subtract(fee), spend.getOutput(0).getValue());
    assertEquals(COIN.subtract(valueToSend), spend.getOutput(1).getValue());
    assertEquals(1, spend.getInputs().size());
    assertEquals(COIN, spend.getInput(0).getValue());
    // Fee is split between the 2 outputs
    SendRequest request2 = SendRequest.to(OTHER_ADDRESS, valueToSend);
    request2.tx.addOutput(valueToSend, OTHER_ADDRESS);
    request2.feePerKb = Transaction.DEFAULT_TX_FEE;
    request2.ensureMinRequiredFee = true;
    request2.recipientsPayFees = true;
    request2.shuffleOutputs = false;
    wallet.completeTx(request2);
    // Hardcoded tx length because actual length may vary depending on actual signature length
    Coin fee2 = request2.feePerKb.multiply(261).divide(1000);
    assertEquals(fee2, request2.tx.getFee());
    Transaction spend2 = request2.tx;
    assertEquals(3, spend2.getOutputs().size());
    assertEquals(valueToSend.subtract(fee2.divide(2)), spend2.getOutput(0).getValue());
    assertEquals(valueToSend.subtract(fee2.divide(2)), spend2.getOutput(1).getValue());
    assertEquals(COIN.subtract(valueToSend.multiply(2)), spend2.getOutput(2).getValue());
    assertEquals(1, spend2.getInputs().size());
    assertEquals(COIN, spend2.getInput(0).getValue());
    // Fee is split between the 3 outputs. Division has a remainder which is taken from the first output
    SendRequest request3 = SendRequest.to(OTHER_ADDRESS, valueToSend);
    request3.tx.addOutput(valueToSend, OTHER_ADDRESS);
    request3.tx.addOutput(valueToSend, OTHER_ADDRESS);
    request3.feePerKb = Transaction.DEFAULT_TX_FEE;
    request3.ensureMinRequiredFee = true;
    request3.recipientsPayFees = true;
    request3.shuffleOutputs = false;
    wallet.completeTx(request3);
    // Hardcoded tx length because actual length may vary depending on actual signature length
    Coin fee3 = request3.feePerKb.multiply(295).divide(1000);
    assertEquals(fee3, request3.tx.getFee());
    Transaction spend3 = request3.tx;
    assertEquals(4, spend3.getOutputs().size());
    // 1st output pays the fee division remainder
    assertEquals(valueToSend.subtract(fee3.divideAndRemainder(3)[0]).subtract(fee3.divideAndRemainder(3)[1]),
            spend3.getOutput(0).getValue());
    assertEquals(valueToSend.subtract(fee3.divide(3)), spend3.getOutput(1).getValue());
    assertEquals(valueToSend.subtract(fee3.divide(3)), spend3.getOutput(2).getValue());
    assertEquals(COIN.subtract(valueToSend.multiply(3)), spend3.getOutput(3).getValue());
    assertEquals(1, spend3.getInputs().size());
    assertEquals(COIN, spend3.getInput(0).getValue());
    // Output when subtracted fee is dust
    // Hardcoded tx length because actual length may vary depending on actual signature length
    Coin fee4 = Transaction.DEFAULT_TX_FEE.multiply(227).divide(1000);
    valueToSend = fee4.add(Transaction.MIN_NONDUST_OUTPUT).subtract(SATOSHI);
    SendRequest request4 = SendRequest.to(OTHER_ADDRESS, valueToSend);
    request4.feePerKb = Transaction.DEFAULT_TX_FEE;
    request4.ensureMinRequiredFee = true;
    request4.recipientsPayFees = true;
    request4.shuffleOutputs = false;
    try {
        wallet.completeTx(request4);
        fail("Expected CouldNotAdjustDownwards exception");
    } catch (Wallet.CouldNotAdjustDownwards e) {
    }
    // Change is dust, so it is incremented to min non dust value. First output value is reduced to compensate.
    // Hardcoded tx length because actual length may vary depending on actual signature length
    Coin fee5 = Transaction.DEFAULT_TX_FEE.multiply(261).divide(1000);
    valueToSend = COIN.divide(2).subtract(Coin.MICROCOIN);
    SendRequest request5 = SendRequest.to(OTHER_ADDRESS, valueToSend);
    request5.tx.addOutput(valueToSend, OTHER_ADDRESS);
    request5.feePerKb = Transaction.DEFAULT_TX_FEE;
    request5.ensureMinRequiredFee = true;
    request5.recipientsPayFees = true;
    request5.shuffleOutputs = false;
    wallet.completeTx(request5);
    assertEquals(fee5, request5.tx.getFee());
    Transaction spend5 = request5.tx;
    assertEquals(3, spend5.getOutputs().size());
    Coin valueSubtractedFromFirstOutput = Transaction.MIN_NONDUST_OUTPUT
            .subtract(COIN.subtract(valueToSend.multiply(2)));
    assertEquals(valueToSend.subtract(fee5.divide(2)).subtract(valueSubtractedFromFirstOutput),
            spend5.getOutput(0).getValue());
    assertEquals(valueToSend.subtract(fee5.divide(2)), spend5.getOutput(1).getValue());
    assertEquals(Transaction.MIN_NONDUST_OUTPUT, spend5.getOutput(2).getValue());
    assertEquals(1, spend5.getInputs().size());
    assertEquals(COIN, spend5.getInput(0).getValue());
    // Change is dust, so it is incremented to min non dust value. First output value is about to be reduced to
    // compensate, but after subtracting some satoshis, first output is dust.
    // Hardcoded tx length because actual length may vary depending on actual signature length
    Coin fee6 = Transaction.DEFAULT_TX_FEE.multiply(261).divide(1000);
    Coin valueToSend1 = fee6.divide(2).add(Transaction.MIN_NONDUST_OUTPUT).add(Coin.MICROCOIN);
    Coin valueToSend2 = COIN.subtract(valueToSend1).subtract(Coin.MICROCOIN.multiply(2));
    SendRequest request6 = SendRequest.to(OTHER_ADDRESS, valueToSend1);
    request6.tx.addOutput(valueToSend2, OTHER_ADDRESS);
    request6.feePerKb = Transaction.DEFAULT_TX_FEE;
    request6.ensureMinRequiredFee = true;
    request6.recipientsPayFees = true;
    request6.shuffleOutputs = false;
    try {
        wallet.completeTx(request6);
        fail("Expected CouldNotAdjustDownwards exception");
    } catch (Wallet.CouldNotAdjustDownwards e) {
    }
}