Java Code Examples for org.bitcoinj.core.Coin#multiply()

The following examples show how to use org.bitcoinj.core.Coin#multiply() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: TxFeeEstimationService.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
public Tuple2<Coin, Integer> getEstimatedFeeAndTxSize(Coin amount,
                                                      FeeService feeService,
                                                      BtcWalletService btcWalletService) {
    Coin txFeePerByte = feeService.getTxFeePerByte();
    // We start with min taker fee size of 260
    int estimatedTxSize = TYPICAL_TX_WITH_1_INPUT_SIZE;
    try {
        estimatedTxSize = getEstimatedTxSize(List.of(amount), estimatedTxSize, txFeePerByte, btcWalletService);
    } catch (InsufficientMoneyException e) {
        log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
                "if the user pays from an external wallet. In that case we use an estimated tx size of {} bytes.", estimatedTxSize);
    }

    Coin txFee = txFeePerByte.multiply(estimatedTxSize);
    log.info("Fee estimation resulted in a tx size of {} bytes and a tx fee of {} Sat.", estimatedTxSize, txFee.value);

    return new Tuple2<>(txFee, estimatedTxSize);
}
 
Example 2
Source File: TxFeeEstimationServiceTest.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
@Test
@Ignore
public void testGetEstimatedTxSize_withSmallTx() throws InsufficientMoneyException {
    List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
    int initialEstimatedTxSize;
    Coin txFeePerByte;
    BtcWalletService btcWalletService = mock(BtcWalletService.class);
    int result;
    int realTxSize;
    Coin txFee;

    initialEstimatedTxSize = 2600;
    txFeePerByte = Coin.valueOf(10);
    realTxSize = 260;

    txFee = txFeePerByte.multiply(initialEstimatedTxSize);
    when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
    result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
    assertEquals(260, result);
}
 
Example 3
Source File: TxFeeEstimationServiceTest.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
@Test
@Ignore
public void testGetEstimatedTxSize_withLargeTx() throws InsufficientMoneyException {
    List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
    int initialEstimatedTxSize;
    Coin txFeePerByte;
    BtcWalletService btcWalletService = mock(BtcWalletService.class);
    int result;
    int realTxSize;
    Coin txFee;

    initialEstimatedTxSize = 260;
    txFeePerByte = Coin.valueOf(10);
    realTxSize = 2600;

    txFee = txFeePerByte.multiply(initialEstimatedTxSize);
    when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);

    // repeated calls to getEstimatedFeeTxSize do not work (returns 0 at second call in loop which cause test to fail)
    result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
    assertEquals(2600, result);
}
 
Example 4
Source File: TxFeeEstimationServiceTest.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
@Test
public void testGetEstimatedTxSize_withDefaultTxSize() throws InsufficientMoneyException {
    List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
    int initialEstimatedTxSize;
    Coin txFeePerByte;
    BtcWalletService btcWalletService = mock(BtcWalletService.class);
    int result;
    int realTxSize;
    Coin txFee;

    initialEstimatedTxSize = 260;
    txFeePerByte = Coin.valueOf(10);
    realTxSize = 260;

    txFee = txFeePerByte.multiply(initialEstimatedTxSize);
    when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
    result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
    assertEquals(260, result);
}
 
Example 5
Source File: TransactionDetailsActivity.java    From guarda-android-wallets with GNU General Public License v3.0 5 votes vote down vote up
private void setValue(long value) {
    Coin coin = Coin.valueOf(value);

    //for tokens where decimals not equals default value = 8
    int shift = decimals - 8;
    if (shift != 0) {
        BigInteger bi = BigInteger.TEN.pow(shift < 0 ? shift * -1 : shift);
        coin = shift < 0 ? coin.multiply(bi.longValue()) : coin.divide(bi.longValue());
    }

    String sum = WalletManager.getFriendlyBalance(coin) + " " + transaction.getTokenTicker().toUpperCase();
    etTrValue.setText(sum);
}
 
Example 6
Source File: GaService.java    From GreenBits with GNU General Public License v3.0 5 votes vote down vote up
private Coin getSpendingLimitAmount() {
    final Coin unconverted = mLimitsData.getCoin("total");
    if (isElements())
        return unconverted.multiply(100);
    if (!mLimitsData.getBool("is_fiat"))
        return unconverted;
    // Fiat class uses SMALLEST_UNIT_EXPONENT units (10^4), our limit is
    // held in 10^2 (e.g. cents) units, hence we * 100 below.
    final Fiat fiatLimit = Fiat.valueOf("???", mLimitsData.getLong("total") * 100);
    return getFiatRate().fiatToCoin(fiatLimit);
}
 
Example 7
Source File: TxFeeEstimationService.java    From bisq with GNU Affero General Public License v3.0 5 votes vote down vote up
@VisibleForTesting
static int getEstimatedTxSize(List<Coin> outputValues,
                              int initialEstimatedTxSize,
                              Coin txFeePerByte,
                              BtcWalletService btcWalletService)
        throws InsufficientMoneyException {
    boolean isInTolerance;
    int estimatedTxSize = initialEstimatedTxSize;
    int realTxSize;
    int counter = 0;
    do {
        Coin txFee = txFeePerByte.multiply(estimatedTxSize);
        realTxSize = btcWalletService.getEstimatedFeeTxSize(outputValues, txFee);
        isInTolerance = isInTolerance(estimatedTxSize, realTxSize, 0.2);
        if (!isInTolerance) {
            estimatedTxSize = realTxSize;
        }
        counter++;
    }
    while (!isInTolerance && counter < MAX_ITERATIONS);
    if (!isInTolerance) {
        log.warn("We could not find a tx which satisfies our tolerance requirement of 20%. " +
                        "realTxSize={}, estimatedTxSize={}",
                realTxSize, estimatedTxSize);
    }
    return estimatedTxSize;
}
 
Example 8
Source File: BtcWalletService.java    From bisq with GNU Affero General Public License v3.0 5 votes vote down vote up
public Transaction getFeeEstimationTransaction(String fromAddress,
                                               String toAddress,
                                               Coin amount,
                                               AddressEntry.Context context)
        throws AddressFormatException, AddressEntryException, InsufficientFundsException {

    Optional<AddressEntry> addressEntry = findAddressEntry(fromAddress, context);
    if (!addressEntry.isPresent())
        throw new AddressEntryException("WithdrawFromAddress is not found in our wallet.");

    checkNotNull(addressEntry.get().getAddress(), "addressEntry.get().getAddress() must nto be null");

    try {
        Coin fee;
        int counter = 0;
        int txSize = 0;
        Transaction tx;
        Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
        do {
            counter++;
            fee = txFeeForWithdrawalPerByte.multiply(txSize);
            SendRequest sendRequest = getSendRequest(fromAddress, toAddress, amount, fee, aesKey, context);
            wallet.completeTx(sendRequest);
            tx = sendRequest.tx;
            txSize = tx.bitcoinSerialize().length;
            printTx("FeeEstimationTransaction", tx);
        }
        while (feeEstimationNotSatisfied(counter, tx));
        if (counter == 10)
            log.error("Could not calculate the fee. Tx=" + tx);

        return tx;
    } catch (InsufficientMoneyException e) {
        throw new InsufficientFundsException("The fees for that transaction exceed the available funds " +
                "or the resulting output value is below the min. dust value:\n" +
                "Missing " + (e.missing != null ? e.missing.toFriendlyString() : "null"));
    }
}
 
Example 9
Source File: BtcWalletService.java    From bisq-core with GNU Affero General Public License v3.0 5 votes vote down vote up
public Transaction getFeeEstimationTransaction(String fromAddress,
                                               String toAddress,
                                               Coin amount,
                                               AddressEntry.Context context)
        throws AddressFormatException, AddressEntryException, InsufficientFundsException {

    Optional<AddressEntry> addressEntry = findAddressEntry(fromAddress, context);
    if (!addressEntry.isPresent())
        throw new AddressEntryException("WithdrawFromAddress is not found in our wallet.");

    checkNotNull(addressEntry.get().getAddress(), "addressEntry.get().getAddress() must nto be null");

    try {
        Coin fee;
        int counter = 0;
        int txSize = 0;
        Transaction tx;
        Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
        do {
            counter++;
            fee = txFeeForWithdrawalPerByte.multiply(txSize);
            SendRequest sendRequest = getSendRequest(fromAddress, toAddress, amount, fee, aesKey, context);
            wallet.completeTx(sendRequest);
            tx = sendRequest.tx;
            txSize = tx.bitcoinSerialize().length;
            printTx("FeeEstimationTransaction", tx);
        }
        while (feeEstimationNotSatisfied(counter, tx));
        if (counter == 10)
            log.error("Could not calculate the fee. Tx=" + tx);

        return tx;
    } catch (InsufficientMoneyException e) {
        throw new InsufficientFundsException("The fees for that transaction exceed the available funds " +
                "or the resulting output value is below the min. dust value:\n" +
                "Missing " + (e.missing != null ? e.missing.toFriendlyString() : "null"));
    }
}
 
Example 10
Source File: TransHistoryAdapter.java    From guarda-android-wallets with GNU General Public License v3.0 4 votes vote down vote up
@Override
    public void onBindViewHolder(TransHistoryItemHolder holder, final int position) {
        final TransactionItem item = getTxByPosition(position);
        Long txSum = item.getValue();
        holder.rootView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null) {
                    listener.OnItemClick(position);
                }
            }
        });

        if (item.getConfirmations() < MIN_CONFIRMATIONS) {
//            enableLoader(holder);
            holder.tvTxStatus.setText(R.string.tx_status_wait);
        } else {
//            disableLoader(holder);
            holder.tvTxStatus.setVisibility(View.GONE);
            holder.viewIndicator.setBackground(view.getContext().getResources().getDrawable(R.drawable.transaction_indicator_green));
            holder.viewIndicator.setVisibility(View.VISIBLE);
        }

        Coin coin = Coin.valueOf(txSum);
        //String sumStr = coin.toPlainString() + " " + sharedManager.getCurrentCurrency().toUpperCase();

        //for tokens where decimals not equals default value = 8
        int shift = getDecimal(item, tokensToRequest) - 8;
        if (shift != 0) {
            BigInteger bi = BigInteger.TEN.pow(shift < 0 ? shift * -1 : shift);
            coin = shift < 0 ? coin.multiply(bi.longValue()) : coin.divide(bi.longValue());
        }

        String sumStr = coin.toPlainString();

        holder.tvTransactionSum.setText(item.isOut() ? "-" + sumStr + " " + item.getTokenTicker() : sumStr + " " + item.getTokenTicker());
        holder.tvDate.setText(CalendarHelper.parseDateToddMMyyyy(item.getTime() * 1000));

//        if (!item.isOut()) {
//            holder.viewIndicator.setBackground(view.getContext().getResources().getDrawable(R.drawable.transaction_indicator_green));
//        } else {
//            holder.viewIndicator.setBackground(view.getContext().getResources().getDrawable(R.drawable.transaction_indicator_red));
//        }

//        holder.tvTokenTicker.setText(item.getTokenTicker());
    }
 
Example 11
Source File: TxFeeEstimationService.java    From bisq with GNU Affero General Public License v3.0 4 votes vote down vote up
private Tuple2<Coin, Integer> getEstimatedFeeAndTxSize(boolean isTaker,
                                                       Coin amount,
                                                       Coin tradeFee,
                                                       FeeService feeService,
                                                       BtcWalletService btcWalletService,
                                                       Preferences preferences) {
    Coin txFeePerByte = feeService.getTxFeePerByte();
    // We start with min taker fee size of 260
    int estimatedTxSize = TYPICAL_TX_WITH_1_INPUT_SIZE;
    try {
        estimatedTxSize = getEstimatedTxSize(List.of(tradeFee, amount), estimatedTxSize, txFeePerByte, btcWalletService);
    } catch (InsufficientMoneyException e) {
        if (isTaker) {
            // if we cannot do the estimation we use the payout tx size
            estimatedTxSize = PAYOUT_TX_SIZE;
        }
        log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
                "if the user pays from an external wallet. In that case we use an estimated tx size of {} bytes.", estimatedTxSize);
    }

    if (!preferences.isPayFeeInBtc()) {
        // If we pay the fee in BSQ we have one input more which adds about 150 bytes
        // TODO: Clarify if there is always just one additional input or if there can be more.
        estimatedTxSize += BSQ_INPUT_INCREASE;
    }

    Coin txFee;
    int size;
    if (isTaker) {
        int averageSize = (estimatedTxSize + DEPOSIT_TX_SIZE) / 2;  // deposit tx has about 320 bytes
        // We use at least the size of the payout tx to not underpay at payout.
        size = Math.max(PAYOUT_TX_SIZE, averageSize);
        txFee = txFeePerByte.multiply(size);
        log.info("Fee estimation resulted in a tx size of {} bytes.\n" +
                "We use an average between the taker fee tx and the deposit tx (320 bytes) which results in {} bytes.\n" +
                "The payout tx has 380 bytes, we use that as our min value. Size for fee calculation is {} bytes.\n" +
                "The tx fee of {} Sat", estimatedTxSize, averageSize, size, txFee.value);
    } else {
        size = estimatedTxSize;
        txFee = txFeePerByte.multiply(size);
        log.info("Fee estimation resulted in a tx size of {} bytes and a tx fee of {} Sat.", size, txFee.value);
    }

    return new Tuple2<>(txFee, size);
}
 
Example 12
Source File: BtcWalletService.java    From bisq-core with GNU Affero General Public License v3.0 4 votes vote down vote up
public Transaction completePreparedCompensationRequestTx(Coin issuanceAmount, Address issuanceAddress, Transaction feeTx, byte[] opReturnData) throws
        TransactionVerificationException, WalletException, InsufficientMoneyException {

    // (BsqFee)tx has following structure:
    // inputs [1-n] BSQ inputs (fee)
    // outputs [0-1] BSQ request fee change output (>= 546 Satoshi)

    // preparedCompensationRequestTx has following structure:
    // inputs [1-n] BSQ inputs for request fee
    // inputs [1-n] BTC inputs for BSQ issuance and miner fee
    // outputs [1] Mandatory BSQ request fee change output (>= 546 Satoshi)
    // outputs [1] Potentially BSQ issuance output (>= 546 Satoshi)
    // outputs [0-1] BTC change output from issuance and miner fee inputs (>= 546 Satoshi)
    // outputs [1] OP_RETURN with opReturnData and amount 0
    // mining fee: BTC mining fee + burned BSQ fee

    Transaction preparedTx = new Transaction(params);
    // Copy inputs from BSQ fee tx
    feeTx.getInputs().forEach(preparedTx::addInput);
    int indexOfBtcFirstInput = feeTx.getInputs().size();

    // Need to be first because issuance is not guaranteed to be valid and would otherwise burn change output!
    // BSQ change outputs from BSQ fee inputs.
    feeTx.getOutputs().forEach(preparedTx::addOutput);

    // BSQ issuance output
    preparedTx.addOutput(issuanceAmount, issuanceAddress);


    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    final int sigSizePerInput = 106;
    // typical size for a tx with 3 inputs
    int txSizeWithUnsignedInputs = 300;
    final Coin txFeePerByte = feeService.getTxFeePerByte();

    Address changeAddress = getFreshAddressEntry().getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");

    final BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    final List<TransactionInput> preparedBsqTxInputs = preparedTx.getInputs();
    final List<TransactionOutput> preparedBsqTxOutputs = preparedTx.getOutputs();
    int numInputs = preparedBsqTxInputs.size();
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }

        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.stream().forEach(tx::addInput);
        preparedBsqTxOutputs.stream().forEach(tx::addOutput);

        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;

        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;

        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);

        resultTx = sendRequest.tx;

        // add OP_RETURN output
        resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));

        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    }
    while (isFeeOutsideTolerance);

    // Sign all BTC inputs
    signAllBtcInputs(indexOfBtcFirstInput, resultTx);

    checkWalletConsistency(wallet);
    verifyTransaction(resultTx);

    // printTx("BTC wallet: Signed tx", resultTx);
    return resultTx;
}
 
Example 13
Source File: BtcWalletService.java    From bisq with GNU Affero General Public License v3.0 4 votes vote down vote up
private Transaction completePreparedProposalTx(Transaction feeTx, byte[] opReturnData,
                                               @Nullable Coin issuanceAmount, @Nullable Address issuanceAddress)
        throws TransactionVerificationException, WalletException, InsufficientMoneyException {

    // (BsqFee)tx has following structure:
    // inputs [1-n] BSQ inputs (fee)
    // outputs [0-1] BSQ request fee change output (>= 546 Satoshi)

    // preparedCompensationRequestTx has following structure:
    // inputs [1-n] BSQ inputs for request fee
    // inputs [1-n] BTC inputs for BSQ issuance and miner fee
    // outputs [1] Mandatory BSQ request fee change output (>= 546 Satoshi)
    // outputs [1] Potentially BSQ issuance output (>= 546 Satoshi) - in case of a issuance tx, otherwise that output does not exist
    // outputs [0-1] BTC change output from issuance and miner fee inputs (>= 546 Satoshi)
    // outputs [1] OP_RETURN with opReturnData and amount 0
    // mining fee: BTC mining fee + burned BSQ fee

    Transaction preparedTx = new Transaction(params);
    // Copy inputs from BSQ fee tx
    feeTx.getInputs().forEach(preparedTx::addInput);
    int indexOfBtcFirstInput = feeTx.getInputs().size();

    // Need to be first because issuance is not guaranteed to be valid and would otherwise burn change output!
    // BSQ change outputs from BSQ fee inputs.
    feeTx.getOutputs().forEach(preparedTx::addOutput);

    // For generic proposals there is no issuance output, for compensation and reimburse requests there is
    if (issuanceAmount != null && issuanceAddress != null) {
        // BSQ issuance output
        preparedTx.addOutput(issuanceAmount, issuanceAddress);
    }

    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    int sigSizePerInput = 106;
    // typical size for a tx with 3 inputs
    int txSizeWithUnsignedInputs = 300;
    Coin txFeePerByte = feeService.getTxFeePerByte();

    Address changeAddress = getFreshAddressEntry().getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");

    BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE),
            preferences.getIgnoreDustThreshold());
    List<TransactionInput> preparedBsqTxInputs = preparedTx.getInputs();
    List<TransactionOutput> preparedBsqTxOutputs = preparedTx.getOutputs();
    int numInputs = preparedBsqTxInputs.size();
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }

        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.forEach(tx::addInput);
        preparedBsqTxOutputs.forEach(tx::addOutput);

        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;

        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;

        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);

        resultTx = sendRequest.tx;

        // add OP_RETURN output
        resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));

        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    }
    while (isFeeOutsideTolerance);

    // Sign all BTC inputs
    signAllBtcInputs(indexOfBtcFirstInput, resultTx);

    checkWalletConsistency(wallet);
    verifyTransaction(resultTx);

    // printTx("BTC wallet: Signed tx", resultTx);
    return resultTx;
}
 
Example 14
Source File: BtcWalletService.java    From bisq with GNU Affero General Public License v3.0 4 votes vote down vote up
private Transaction addInputsForMinerFee(Transaction preparedTx, byte[] opReturnData) throws InsufficientMoneyException {
    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    int sigSizePerInput = 106;
    // typical size for a tx with 3 inputs
    int txSizeWithUnsignedInputs = 300;
    Coin txFeePerByte = feeService.getTxFeePerByte();

    Address changeAddress = getFreshAddressEntry().getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");

    BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE),
            preferences.getIgnoreDustThreshold());
    List<TransactionInput> preparedBsqTxInputs = preparedTx.getInputs();
    List<TransactionOutput> preparedBsqTxOutputs = preparedTx.getOutputs();
    int numInputs = preparedBsqTxInputs.size();
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }

        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.forEach(tx::addInput);
        preparedBsqTxOutputs.forEach(tx::addOutput);

        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;

        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;

        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);

        resultTx = sendRequest.tx;

        // add OP_RETURN output
        resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));

        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    }
    while (isFeeOutsideTolerance);
    return resultTx;
}
 
Example 15
Source File: BtcWalletService.java    From bisq with GNU Affero General Public License v3.0 4 votes vote down vote up
public Transaction getFeeEstimationTransactionForMultipleAddresses(Set<String> fromAddresses,
                                                                   Coin amount)
        throws AddressFormatException, AddressEntryException, InsufficientFundsException {
    Set<AddressEntry> addressEntries = fromAddresses.stream()
            .map(address -> {
                Optional<AddressEntry> addressEntryOptional = findAddressEntry(address, AddressEntry.Context.AVAILABLE);
                if (!addressEntryOptional.isPresent())
                    addressEntryOptional = findAddressEntry(address, AddressEntry.Context.OFFER_FUNDING);
                if (!addressEntryOptional.isPresent())
                    addressEntryOptional = findAddressEntry(address, AddressEntry.Context.TRADE_PAYOUT);
                if (!addressEntryOptional.isPresent())
                    addressEntryOptional = findAddressEntry(address, AddressEntry.Context.ARBITRATOR);
                return addressEntryOptional;
            })
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.toSet());
    if (addressEntries.isEmpty())
        throw new AddressEntryException("No Addresses for withdraw  found in our wallet");

    try {
        Coin fee;
        int counter = 0;
        int txSize = 0;
        Transaction tx;
        Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
        do {
            counter++;
            fee = txFeeForWithdrawalPerByte.multiply(txSize);
            // We use a dummy address for the output
            final String dummyReceiver = getFreshAddressEntry().getAddressString();
            SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, dummyReceiver, amount, fee, null, aesKey);
            wallet.completeTx(sendRequest);
            tx = sendRequest.tx;
            txSize = tx.bitcoinSerialize().length;
            printTx("FeeEstimationTransactionForMultipleAddresses", tx);
        }
        while (feeEstimationNotSatisfied(counter, tx));
        if (counter == 10)
            log.error("Could not calculate the fee. Tx=" + tx);

        return tx;
    } catch (InsufficientMoneyException e) {
        throw new InsufficientFundsException("The fees for that transaction exceed the available funds " +
                "or the resulting output value is below the min. dust value:\n" +
                "Missing " + (e.missing != null ? e.missing.toFriendlyString() : "null"));
    }
}
 
Example 16
Source File: BtcWalletService.java    From bisq-core with GNU Affero General Public License v3.0 4 votes vote down vote up
public Transaction getFeeEstimationTransactionForMultipleAddresses(Set<String> fromAddresses,
                                                                   Coin amount)
        throws AddressFormatException, AddressEntryException, InsufficientFundsException {
    Set<AddressEntry> addressEntries = fromAddresses.stream()
            .map(address -> {
                Optional<AddressEntry> addressEntryOptional = findAddressEntry(address, AddressEntry.Context.AVAILABLE);
                if (!addressEntryOptional.isPresent())
                    addressEntryOptional = findAddressEntry(address, AddressEntry.Context.OFFER_FUNDING);
                if (!addressEntryOptional.isPresent())
                    addressEntryOptional = findAddressEntry(address, AddressEntry.Context.TRADE_PAYOUT);
                if (!addressEntryOptional.isPresent())
                    addressEntryOptional = findAddressEntry(address, AddressEntry.Context.ARBITRATOR);
                return addressEntryOptional;
            })
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.toSet());
    if (addressEntries.isEmpty())
        throw new AddressEntryException("No Addresses for withdraw  found in our wallet");

    try {
        Coin fee;
        int counter = 0;
        int txSize = 0;
        Transaction tx;
        Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
        do {
            counter++;
            fee = txFeeForWithdrawalPerByte.multiply(txSize);
            // We use a dummy address for the output
            final String dummyReceiver = getFreshAddressEntry().getAddressString();
            SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, dummyReceiver, amount, fee, null, aesKey);
            wallet.completeTx(sendRequest);
            tx = sendRequest.tx;
            txSize = tx.bitcoinSerialize().length;
            printTx("FeeEstimationTransactionForMultipleAddresses", tx);
        }
        while (feeEstimationNotSatisfied(counter, tx));
        if (counter == 10)
            log.error("Could not calculate the fee. Tx=" + tx);

        return tx;
    } catch (InsufficientMoneyException e) {
        throw new InsufficientFundsException("The fees for that transaction exceed the available funds " +
                "or the resulting output value is below the min. dust value:\n" +
                "Missing " + (e.missing != null ? e.missing.toFriendlyString() : "null"));
    }
}
 
Example 17
Source File: BtcWalletService.java    From bisq-core with GNU Affero General Public License v3.0 4 votes vote down vote up
private Transaction addInputsForMinerFee(Transaction preparedTx, byte[] opReturnData) throws InsufficientMoneyException {
    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    final int sigSizePerInput = 106;
    // typical size for a tx with 3 inputs
    int txSizeWithUnsignedInputs = 300;
    final Coin txFeePerByte = feeService.getTxFeePerByte();

    Address changeAddress = getFreshAddressEntry().getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");

    final BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    final List<TransactionInput> preparedBsqTxInputs = preparedTx.getInputs();
    final List<TransactionOutput> preparedBsqTxOutputs = preparedTx.getOutputs();
    int numInputs = preparedBsqTxInputs.size();
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }

        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.forEach(tx::addInput);
        preparedBsqTxOutputs.forEach(tx::addOutput);

        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;

        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;

        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);

        resultTx = sendRequest.tx;

        // add OP_RETURN output
        resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));

        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    }
    while (isFeeOutsideTolerance);
    return resultTx;
}