Java Code Examples for org.bitcoinj.wallet.SendRequest#forTx()

The following examples show how to use org.bitcoinj.wallet.SendRequest#forTx() . 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: TransactionOutputTest.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testMultiSigOutputToString() throws Exception {
    sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, Coin.COIN);
    ECKey myKey = new ECKey();
    this.wallet.importKey(myKey);

    // Simulate another signatory
    ECKey otherKey = new ECKey();

    // Create multi-sig transaction
    Transaction multiSigTransaction = new Transaction(PARAMS);
    ImmutableList<ECKey> keys = ImmutableList.of(myKey, otherKey);

    Script scriptPubKey = ScriptBuilder.createMultiSigOutputScript(2, keys);
    multiSigTransaction.addOutput(Coin.COIN, scriptPubKey);

    SendRequest req = SendRequest.forTx(multiSigTransaction);
    this.wallet.completeTx(req);
    TransactionOutput multiSigTransactionOutput = multiSigTransaction.getOutput(0);

    assertThat(multiSigTransactionOutput.toString(), CoreMatchers.containsString("CHECKMULTISIG"));
}
 
Example 2
Source File: TransactionOutputTest.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testMultiSigOutputToString() throws Exception {
    sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, Coin.COIN);
    ECKey myKey = new ECKey();
    this.wallet.importKey(myKey);

    // Simulate another signatory
    ECKey otherKey = new ECKey();

    // Create multi-sig transaction
    Transaction multiSigTransaction = new Transaction(UNITTEST);
    ImmutableList<ECKey> keys = ImmutableList.of(myKey, otherKey);

    Script scriptPubKey = ScriptBuilder.createMultiSigOutputScript(2, keys);
    multiSigTransaction.addOutput(Coin.COIN, scriptPubKey);

    SendRequest req = SendRequest.forTx(multiSigTransaction);
    this.wallet.completeTx(req);
    TransactionOutput multiSigTransactionOutput = multiSigTransaction.getOutput(0);

    assertThat(multiSigTransactionOutput.toString(), CoreMatchers.containsString("CHECKMULTISIG"));
}
 
Example 3
Source File: BsqWalletService.java    From bisq-core with GNU Affero General Public License v3.0 6 votes vote down vote up
public Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount)
        throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException {
    Transaction tx = new Transaction(params);
    checkArgument(Restrictions.isAboveDust(receiverAmount),
            "The amount is too low (dust limit).");
    tx.addOutput(receiverAmount, Address.fromBase58(params, receiverAddress));

    SendRequest sendRequest = SendRequest.forTx(tx);
    sendRequest.fee = Coin.ZERO;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;
    sendRequest.aesKey = aesKey;
    sendRequest.shuffleOutputs = false;
    sendRequest.signInputs = false;
    sendRequest.ensureMinRequiredFee = false;
    sendRequest.changeAddress = getUnusedAddress();
    try {
        wallet.completeTx(sendRequest);
    } catch (InsufficientMoneyException e) {
        throw new InsufficientBsqException(e.missing);
    }
    checkWalletConsistency(wallet);
    verifyTransaction(tx);
    // printTx("prepareSendTx", tx);
    return tx;
}
 
Example 4
Source File: TransactionOutputTest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testMultiSigOutputToString() throws Exception {
    sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, Coin.COIN);
    ECKey myKey = new ECKey();
    this.wallet.importKey(myKey);

    // Simulate another signatory
    ECKey otherKey = new ECKey();

    // Create multi-sig transaction
    Transaction multiSigTransaction = new Transaction(PARAMS);
    ImmutableList<ECKey> keys = ImmutableList.of(myKey, otherKey);

    Script scriptPubKey = ScriptBuilder.createMultiSigOutputScript(2, keys);
    multiSigTransaction.addOutput(Coin.COIN, scriptPubKey);

    SendRequest req = SendRequest.forTx(multiSigTransaction);
    this.wallet.completeTx(req);
    TransactionOutput multiSigTransactionOutput = multiSigTransaction.getOutput(0);

    assertThat(multiSigTransactionOutput.toString(), CoreMatchers.containsString("CHECKMULTISIG"));
}
 
Example 5
Source File: BtcWalletService.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
public int getEstimatedFeeTxSize(List<Coin> outputValues, Coin txFee)
        throws InsufficientMoneyException, AddressFormatException {
    Transaction transaction = new Transaction(params);
    Address dummyAddress = wallet.currentReceiveKey().toAddress(params);
    outputValues.forEach(outputValue -> transaction.addOutput(outputValue, dummyAddress));

    SendRequest sendRequest = SendRequest.forTx(transaction);
    sendRequest.shuffleOutputs = false;
    sendRequest.aesKey = aesKey;
    sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE),
            preferences.getIgnoreDustThreshold());
    sendRequest.fee = txFee;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;
    sendRequest.changeAddress = dummyAddress;
    wallet.completeTx(sendRequest);
    return transaction.bitcoinSerialize().length;
}
 
Example 6
Source File: BsqWalletService.java    From bisq with GNU Affero General Public License v3.0 5 votes vote down vote up
private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector)
        throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException {
    daoKillSwitch.assertDaoIsNotDisabled();
    Transaction tx = new Transaction(params);
    checkArgument(Restrictions.isAboveDust(receiverAmount),
            "The amount is too low (dust limit).");
    tx.addOutput(receiverAmount, Address.fromBase58(params, receiverAddress));

    SendRequest sendRequest = SendRequest.forTx(tx);
    sendRequest.fee = Coin.ZERO;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;
    sendRequest.aesKey = aesKey;
    sendRequest.shuffleOutputs = false;
    sendRequest.signInputs = false;
    sendRequest.changeAddress = getChangeAddress();
    sendRequest.coinSelector = coinSelector;
    try {
        wallet.completeTx(sendRequest);
        checkWalletConsistency(wallet);
        verifyTransaction(tx);
        // printTx("prepareSendTx", tx);

        // Tx has as first output BSQ and an optional second BSQ change output.
        // At that stage we do not have added the BTC inputs so there is no BTC change output here.
        if (tx.getOutputs().size() == 2) {
            Coin bsqChangeOutputValue = tx.getOutputs().get(1).getValue();
            if (!Restrictions.isAboveDust(bsqChangeOutputValue)) {
                String msg = "BSQ change output is below dust limit. outputValue=" + bsqChangeOutputValue.value / 100 + " BSQ";
                log.warn(msg);
                throw new BsqChangeBelowDustException(msg, bsqChangeOutputValue);
            }
        }

        return tx;
    } catch (InsufficientMoneyException e) {
        log.error(e.toString());
        throw new InsufficientBsqException(e.missing);
    }
}
 
Example 7
Source File: TradeWalletService.java    From bisq with GNU Affero General Public License v3.0 5 votes vote down vote up
private void addAvailableInputsAndChangeOutputs(Transaction transaction,
                                                Address address,
                                                Address changeAddress) throws WalletException {
    SendRequest sendRequest = null;
    try {
        // Let the framework do the work to find the right inputs
        sendRequest = SendRequest.forTx(transaction);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // We use a fixed fee
        sendRequest.fee = Coin.ZERO;
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;
        // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation)
        sendRequest.coinSelector = new BtcCoinSelector(address, preferences.getIgnoreDustThreshold());
        // We use always the same address in a trade for all transactions
        sendRequest.changeAddress = changeAddress;
        // With the usage of completeTx() we get all the work done with fee calculation, validation and coin selection.
        // We don't commit that tx to the wallet as it will be changed later and it's not signed yet.
        // So it will not change the wallet balance.
        checkNotNull(wallet, "wallet must not be null");
        wallet.completeTx(sendRequest);
    } catch (Throwable t) {
        if (sendRequest != null && sendRequest.tx != null) {
            log.warn("addAvailableInputsAndChangeOutputs: sendRequest.tx={}, sendRequest.tx.getOutputs()={}",
                    sendRequest.tx, sendRequest.tx.getOutputs());
        }

        throw new WalletException(t);
    }
}
 
Example 8
Source File: PaymentChannelServerState.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
protected synchronized SendRequest makeUnsignedChannelContract(Coin valueToMe) {
    Transaction tx = new Transaction(wallet.getParams());
    if (!getTotalValue().subtract(valueToMe).equals(Coin.ZERO)) {
        tx.addOutput(getTotalValue().subtract(valueToMe), getClientKey().toAddress(wallet.getParams()));
    }
    tx.addInput(contract.getOutput(0));
    return SendRequest.forTx(tx);
}
 
Example 9
Source File: TradeWalletService.java    From bisq-core with GNU Affero General Public License v3.0 5 votes vote down vote up
private void addAvailableInputsAndChangeOutputs(Transaction transaction, Address address, Address changeAddress, Coin txFee) throws WalletException {
    SendRequest sendRequest = null;
    try {
        // Lets let the framework do the work to find the right inputs
        sendRequest = SendRequest.forTx(transaction);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // We use a fixed fee
        sendRequest.fee = txFee;
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;
        // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation)
        sendRequest.coinSelector = new BtcCoinSelector(address);
        // We use always the same address in a trade for all transactions
        sendRequest.changeAddress = changeAddress;
        // With the usage of completeTx() we get all the work done with fee calculation, validation and coin selection.
        // We don't commit that tx to the wallet as it will be changed later and it's not signed yet.
        // So it will not change the wallet balance.
        checkNotNull(wallet, "wallet must not be null");
        wallet.completeTx(sendRequest);
    } catch (Throwable t) {
        if (sendRequest != null && sendRequest.tx != null)
            log.warn("addAvailableInputsAndChangeOutputs: sendRequest.tx={}, sendRequest.tx.getOutputs()={}", sendRequest.tx, sendRequest.tx.getOutputs());

        throw new WalletException(t);
    }
}
 
Example 10
Source File: TradeWalletService.java    From bisq-core with GNU Affero General Public License v3.0 5 votes vote down vote up
public Transaction estimateBtcTradingFeeTxSize(Address fundingAddress,
                                               Address reservedForTradeAddress,
                                               Address changeAddress,
                                               Coin reservedFundsForOffer,
                                               boolean useSavingsWallet,
                                               Coin tradingFee,
                                               Coin txFee,
                                               String feeReceiverAddresses)
        throws InsufficientMoneyException, AddressFormatException {
    Transaction tradingFeeTx = new Transaction(params);
    tradingFeeTx.addOutput(tradingFee, Address.fromBase58(params, feeReceiverAddresses));
    tradingFeeTx.addOutput(reservedFundsForOffer, reservedForTradeAddress);

    SendRequest sendRequest = SendRequest.forTx(tradingFeeTx);
    sendRequest.shuffleOutputs = false;
    sendRequest.aesKey = aesKey;
    if (useSavingsWallet)
        sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    else
        sendRequest.coinSelector = new BtcCoinSelector(fundingAddress);

    sendRequest.fee = txFee;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;
    sendRequest.changeAddress = changeAddress;
    checkNotNull(wallet, "Wallet must not be null");
    log.info("estimateBtcTradingFeeTxSize");
    wallet.completeTx(sendRequest);
    return tradingFeeTx;
}
 
Example 11
Source File: PaymentChannelServerState.java    From bcm-android with GNU General Public License v3.0 5 votes vote down vote up
protected synchronized SendRequest makeUnsignedChannelContract(Coin valueToMe) {
    Transaction tx = new Transaction(wallet.getParams());
    if (!getTotalValue().subtract(valueToMe).equals(Coin.ZERO)) {
        tx.addOutput(getTotalValue().subtract(valueToMe), LegacyAddress.fromKey(wallet.getParams(), getClientKey()));
    }
    tx.addInput(contract.getOutput(0));
    return SendRequest.forTx(tx);
}
 
Example 12
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 13
Source File: TradeWalletService.java    From bisq-core with GNU Affero General Public License v3.0 4 votes vote down vote up
public Transaction completeBsqTradingFeeTx(Transaction preparedBsqTx,
                                           Address fundingAddress,
                                           Address reservedForTradeAddress,
                                           Address changeAddress,
                                           Coin reservedFundsForOffer,
                                           boolean useSavingsWallet,
                                           Coin txFee) throws
        TransactionVerificationException, WalletException,
        InsufficientMoneyException, AddressFormatException {

    log.debug("preparedBsqTx " + preparedBsqTx.toString());
    log.debug("fundingAddress " + fundingAddress.toString());
    log.debug("changeAddress " + changeAddress.toString());
    log.debug("reservedFundsForOffer " + reservedFundsForOffer.toPlainString());
    log.debug("useSavingsWallet " + useSavingsWallet);
    log.debug("txFee " + txFee.toPlainString());

    // preparedBsqTx has following structure:
    // inputs [1-n] BSQ inputs
    // outputs [0-1] BSQ change output
    // mining fee: burned BSQ fee

    // We add BTC mining fee. Result tx looks like:
    // inputs [1-n] BSQ inputs
    // inputs [1-n] BTC inputs
    // outputs [0-1] BSQ change output
    // outputs [1] BTC reservedForTrade output
    // outputs [0-1] BTC change output
    // mining fee: BTC mining fee + burned BSQ fee

    // In case of txs for burned BSQ fees we have no receiver output and it might be that there is no change outputs
    // We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input
    // for BTC to force an additional change output.

    final int preparedBsqTxInputsSize = preparedBsqTx.getInputs().size();


    // the reserved amount we need for the trade we send to our trade reservedForTradeAddress
    preparedBsqTx.addOutput(reservedFundsForOffer, reservedForTradeAddress);

    // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
    // wait for 1 confirmation)
    // In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet)

    // WalletService.printTx("preparedBsqTx", preparedBsqTx);
    SendRequest sendRequest = SendRequest.forTx(preparedBsqTx);
    sendRequest.shuffleOutputs = false;
    sendRequest.aesKey = aesKey;
    if (useSavingsWallet)
        sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    else
        sendRequest.coinSelector = new BtcCoinSelector(fundingAddress);
    // We use a fixed fee
    sendRequest.fee = txFee;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;

    sendRequest.signInputs = false;

    // Change is optional in case of overpay or use of funds from savings wallet
    sendRequest.changeAddress = changeAddress;

    checkNotNull(wallet, "Wallet must not be null");
    wallet.completeTx(sendRequest);
    Transaction resultTx = sendRequest.tx;

    // Sign all BTC inputs
    for (int i = preparedBsqTxInputsSize; i < resultTx.getInputs().size(); i++) {
        TransactionInput txIn = resultTx.getInputs().get(i);
        checkArgument(txIn.getConnectedOutput() != null && txIn.getConnectedOutput().isMine(wallet),
                "txIn.getConnectedOutput() is not in our wallet. That must not happen.");
        WalletService.signTransactionInput(wallet, aesKey, resultTx, txIn, i);
        WalletService.checkScriptSig(resultTx, txIn, i);
    }

    WalletService.checkWalletConsistency(wallet);
    WalletService.verifyTransaction(resultTx);

    WalletService.printTx(Res.getBaseCurrencyCode() + " 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
public Transaction createRefundPayoutTx(Coin buyerAmount,
                                        Coin sellerAmount,
                                        Coin fee,
                                        String buyerAddressString,
                                        String sellerAddressString)
        throws AddressFormatException, InsufficientMoneyException, WalletException, TransactionVerificationException {
    Transaction tx = new Transaction(params);
    Preconditions.checkArgument(buyerAmount.add(sellerAmount).isPositive(),
            "The sellerAmount + buyerAmount must be positive.");
    // buyerAmount can be 0
    if (buyerAmount.isPositive()) {
        Preconditions.checkArgument(Restrictions.isAboveDust(buyerAmount),
                "The buyerAmount is too low (dust limit).");

        tx.addOutput(buyerAmount, Address.fromBase58(params, buyerAddressString));
    }
    // sellerAmount can be 0
    if (sellerAmount.isPositive()) {
        Preconditions.checkArgument(Restrictions.isAboveDust(sellerAmount),
                "The sellerAmount is too low (dust limit).");

        tx.addOutput(sellerAmount, Address.fromBase58(params, sellerAddressString));
    }

    SendRequest sendRequest = SendRequest.forTx(tx);
    sendRequest.fee = fee;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;
    sendRequest.aesKey = aesKey;
    sendRequest.shuffleOutputs = false;
    sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE),
            preferences.getIgnoreDustThreshold());
    sendRequest.changeAddress = getFreshAddressEntry().getAddress();

    checkNotNull(wallet);
    wallet.completeTx(sendRequest);

    Transaction resultTx = sendRequest.tx;
    checkWalletConsistency(wallet);
    verifyTransaction(resultTx);

    WalletService.printTx("createRefundPayoutTx", resultTx);

    return resultTx;
}
 
Example 15
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 16
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 17
Source File: PaymentChannelV1ClientState.java    From green_android with GNU General Public License v3.0 4 votes vote down vote up
/**
 * Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
 * time using {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction} and
 * {@link PaymentChannelV1ClientState#getContract()}.
 * By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.
 * @param userKey Key derived from a user password, needed for any signing when the wallet is encrypted.
 *                The wallet KeyCrypter is assumed.
 * @param clientChannelProperties Modify the channel's configuration.
 *
 * @throws ValueOutOfRangeException   if the value being used is too small to be accepted by the network
 * @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
 */
@Override
public synchronized void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException {
    final NetworkParameters params = wallet.getParams();
    Transaction template = new Transaction(params);
    // We always place the client key before the server key because, if either side wants some privacy, they can
    // use a fresh key for the the multisig contract and nowhere else
    List<ECKey> keys = Lists.newArrayList(myKey, serverKey);
    // There is also probably a change output, but we don't bother shuffling them as it's obvious from the
    // format which one is the change. If we start obfuscating the change output better in future this may
    // be worth revisiting.
    TransactionOutput multisigOutput = template.addOutput(totalValue, ScriptBuilder.createMultiSigOutputScript(2, keys));
    if (multisigOutput.isDust())
        throw new ValueOutOfRangeException("totalValue too small to use");
    SendRequest req = SendRequest.forTx(template);
    req.coinSelector = AllowUnconfirmedCoinSelector.get();
    req.shuffleOutputs = false;   // TODO: Fix things so shuffling is usable.
    req = clientChannelProperties.modifyContractSendRequest(req);
    if (userKey != null) req.aesKey = userKey;
    wallet.completeTx(req);
    Coin multisigFee = req.tx.getFee();
    multisigContract = req.tx;
    // Build a refund transaction that protects us in the case of a bad server that's just trying to cause havoc
    // by locking up peoples money (perhaps as a precursor to a ransom attempt). We time lock it so the server
    // has an assurance that we cannot take back our money by claiming a refund before the channel closes - this
    // relies on the fact that since Bitcoin 0.8 time locked transactions are non-final. This will need to change
    // in future as it breaks the intended design of timelocking/tx replacement, but for now it simplifies this
    // specific protocol somewhat.
    refundTx = new Transaction(params);
    // don't disable lock time. the sequence will be included in the server's signature and thus won't be changeable.
    // by using this sequence value, we avoid extra full replace-by-fee and relative lock time processing.
    refundTx.addInput(multisigOutput).setSequenceNumber(TransactionInput.NO_SEQUENCE - 1L);
    refundTx.setLockTime(expiryTime);
    if (Context.get().isEnsureMinRequiredFee()) {
        // Must pay min fee.
        final Coin valueAfterFee = totalValue.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
        if (Transaction.MIN_NONDUST_OUTPUT.compareTo(valueAfterFee) > 0)
            throw new ValueOutOfRangeException("totalValue too small to use");
        refundTx.addOutput(valueAfterFee, myKey.toAddress(params));
        refundFees = multisigFee.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
    } else {
        refundTx.addOutput(totalValue, myKey.toAddress(params));
        refundFees = multisigFee;
    }
    refundTx.getConfidence().setSource(TransactionConfidence.Source.SELF);
    log.info("initiated channel with multi-sig contract {}, refund {}", multisigContract.getHashAsString(),
            refundTx.getHashAsString());
    stateMachine.transition(State.INITIATED);
    // Client should now call getIncompleteRefundTransaction() and send it to the server.
}
 
Example 18
Source File: PaymentChannelV1ClientState.java    From bcm-android with GNU General Public License v3.0 4 votes vote down vote up
/**
 * Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
 * time using {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction} and
 * {@link PaymentChannelV1ClientState#getContract()}.
 * By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.
 *
 * @param userKey                 Key derived from a user password, needed for any signing when the wallet is encrypted.
 *                                The wallet KeyCrypter is assumed.
 * @param clientChannelProperties Modify the channel's configuration.
 * @throws ValueOutOfRangeException   if the value being used is too small to be accepted by the network
 * @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
 */
@Override
public synchronized void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException {
    final NetworkParameters params = wallet.getParams();
    Transaction template = new Transaction(params);
    // We always place the client key before the server key because, if either side wants some privacy, they can
    // use a fresh key for the the multisig contract and nowhere else
    List<ECKey> keys = Lists.newArrayList(myKey, serverKey);
    // There is also probably a change output, but we don't bother shuffling them as it's obvious from the
    // format which one is the change. If we start obfuscating the change output better in future this may
    // be worth revisiting.
    TransactionOutput multisigOutput = template.addOutput(totalValue, ScriptBuilder.createMultiSigOutputScript(2, keys));
    if (multisigOutput.isDust())
        throw new ValueOutOfRangeException("totalValue too small to use");
    SendRequest req = SendRequest.forTx(template);
    req.coinSelector = AllowUnconfirmedCoinSelector.get();
    req.shuffleOutputs = false;   // TODO: Fix things so shuffling is usable.
    req = clientChannelProperties.modifyContractSendRequest(req);
    if (userKey != null)
        req.aesKey = userKey;
    wallet.completeTx(req);
    Coin multisigFee = req.tx.getFee();
    multisigContract = req.tx;
    // Build a refund transaction that protects us in the case of a bad server that's just trying to cause havoc
    // by locking up peoples money (perhaps as a precursor to a ransom attempt). We time lock it so the server
    // has an assurance that we cannot take back our money by claiming a refund before the channel closes - this
    // relies on the fact that since Bitcoin 0.8 time locked transactions are non-final. This will need to change
    // in future as it breaks the intended design of timelocking/tx replacement, but for now it simplifies this
    // specific protocol somewhat.
    refundTx = new Transaction(params);
    // don't disable lock time. the sequence will be included in the server's signature and thus won't be changeable.
    // by using this sequence value, we avoid extra full replace-by-fee and relative lock time processing.
    refundTx.addInput(multisigOutput).setSequenceNumber(TransactionInput.NO_SEQUENCE - 1L);
    refundTx.setLockTime(expiryTime);
    if (Context.get().isEnsureMinRequiredFee()) {
        // Must pay min fee.
        final Coin valueAfterFee = totalValue.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
        if (Transaction.MIN_NONDUST_OUTPUT.compareTo(valueAfterFee) > 0)
            throw new ValueOutOfRangeException("totalValue too small to use");
        refundTx.addOutput(valueAfterFee, LegacyAddress.fromKey(params, myKey));
        refundFees = multisigFee.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
    } else {
        refundTx.addOutput(totalValue, LegacyAddress.fromKey(params, myKey));
        refundFees = multisigFee;
    }
    refundTx.getConfidence().setSource(TransactionConfidence.Source.SELF);
    log.info("initiated channel with multi-sig contract {}, refund {}", multisigContract.getHashAsString(),
            refundTx.getHashAsString());
    stateMachine.transition(State.INITIATED);
    // Client should now call getIncompleteRefundTransaction() and send it to the server.
}
 
Example 19
Source File: TradeWalletService.java    From bisq with GNU Affero General Public License v3.0 4 votes vote down vote up
public Transaction completeBsqTradingFeeTx(Transaction preparedBsqTx,
                                           Address fundingAddress,
                                           Address reservedForTradeAddress,
                                           Address changeAddress,
                                           Coin reservedFundsForOffer,
                                           boolean useSavingsWallet,
                                           Coin txFee)
        throws TransactionVerificationException, WalletException, InsufficientMoneyException, AddressFormatException {
    // preparedBsqTx has following structure:
    // inputs [1-n] BSQ inputs
    // outputs [0-1] BSQ change output
    // mining fee: burned BSQ fee

    // We add BTC mining fee. Result tx looks like:
    // inputs [1-n] BSQ inputs
    // inputs [1-n] BTC inputs
    // outputs [0-1] BSQ change output
    // outputs [1] BTC reservedForTrade output
    // outputs [0-1] BTC change output
    // mining fee: BTC mining fee + burned BSQ fee

    // In case of txs for burned BSQ fees we have no receiver output and it might be that there are no change outputs
    // We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input
    // for BTC to force an additional change output.

    final int preparedBsqTxInputsSize = preparedBsqTx.getInputs().size();


    // the reserved amount we need for the trade we send to our trade reservedForTradeAddress
    preparedBsqTx.addOutput(reservedFundsForOffer, reservedForTradeAddress);

    // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
    // wait for 1 confirmation)
    // In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet)

    // If BSQ trade fee > reservedFundsForOffer we would create a BSQ output instead of a BTC output.
    // As the min. reservedFundsForOffer is 0.001 BTC which is 1000 BSQ this is an unrealistic scenario and not
    // handled atm (if BTC price is 1M USD and BSQ price is 0.1 USD, then fee would be 10% which still is unrealistic).

    // WalletService.printTx("preparedBsqTx", preparedBsqTx);
    SendRequest sendRequest = SendRequest.forTx(preparedBsqTx);
    sendRequest.shuffleOutputs = false;
    sendRequest.aesKey = aesKey;
    if (useSavingsWallet) {
        sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE),
                preferences.getIgnoreDustThreshold());
    } else {
        sendRequest.coinSelector = new BtcCoinSelector(fundingAddress, preferences.getIgnoreDustThreshold());
    }
    // We use a fixed fee
    sendRequest.fee = txFee;
    sendRequest.feePerKb = Coin.ZERO;
    sendRequest.ensureMinRequiredFee = false;

    sendRequest.signInputs = false;

    // Change is optional in case of overpay or use of funds from savings wallet
    sendRequest.changeAddress = changeAddress;

    checkNotNull(wallet, "Wallet must not be null");
    wallet.completeTx(sendRequest);
    Transaction resultTx = sendRequest.tx;
    removeDust(resultTx);

    // Sign all BTC inputs
    for (int i = preparedBsqTxInputsSize; i < resultTx.getInputs().size(); i++) {
        TransactionInput txIn = resultTx.getInputs().get(i);
        checkArgument(txIn.getConnectedOutput() != null &&
                        txIn.getConnectedOutput().isMine(wallet),
                "txIn.getConnectedOutput() is not in our wallet. That must not happen.");
        WalletService.signTransactionInput(wallet, aesKey, resultTx, txIn, i);
        WalletService.checkScriptSig(resultTx, txIn, i);
    }

    WalletService.checkWalletConsistency(wallet);
    WalletService.verifyTransaction(resultTx);

    WalletService.printTx(Res.getBaseCurrencyCode() + " wallet: Signed tx", resultTx);
    return resultTx;
}
 
Example 20
Source File: PaymentChannelV1ClientState.java    From GreenBits with GNU General Public License v3.0 4 votes vote down vote up
/**
 * Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
 * time using {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction} and
 * {@link PaymentChannelV1ClientState#getContract()}.
 * By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.
 * @param userKey Key derived from a user password, needed for any signing when the wallet is encrypted.
 *                The wallet KeyCrypter is assumed.
 * @param clientChannelProperties Modify the channel's configuration.
 *
 * @throws ValueOutOfRangeException   if the value being used is too small to be accepted by the network
 * @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
 */
@Override
public synchronized void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException {
    final NetworkParameters params = wallet.getParams();
    Transaction template = new Transaction(params);
    // We always place the client key before the server key because, if either side wants some privacy, they can
    // use a fresh key for the the multisig contract and nowhere else
    List<ECKey> keys = Lists.newArrayList(myKey, serverKey);
    // There is also probably a change output, but we don't bother shuffling them as it's obvious from the
    // format which one is the change. If we start obfuscating the change output better in future this may
    // be worth revisiting.
    TransactionOutput multisigOutput = template.addOutput(totalValue, ScriptBuilder.createMultiSigOutputScript(2, keys));
    if (multisigOutput.isDust())
        throw new ValueOutOfRangeException("totalValue too small to use");
    SendRequest req = SendRequest.forTx(template);
    req.coinSelector = AllowUnconfirmedCoinSelector.get();
    req.shuffleOutputs = false;   // TODO: Fix things so shuffling is usable.
    req = clientChannelProperties.modifyContractSendRequest(req);
    if (userKey != null) req.aesKey = userKey;
    wallet.completeTx(req);
    Coin multisigFee = req.tx.getFee();
    multisigContract = req.tx;
    // Build a refund transaction that protects us in the case of a bad server that's just trying to cause havoc
    // by locking up peoples money (perhaps as a precursor to a ransom attempt). We time lock it so the server
    // has an assurance that we cannot take back our money by claiming a refund before the channel closes - this
    // relies on the fact that since Bitcoin 0.8 time locked transactions are non-final. This will need to change
    // in future as it breaks the intended design of timelocking/tx replacement, but for now it simplifies this
    // specific protocol somewhat.
    refundTx = new Transaction(params);
    // don't disable lock time. the sequence will be included in the server's signature and thus won't be changeable.
    // by using this sequence value, we avoid extra full replace-by-fee and relative lock time processing.
    refundTx.addInput(multisigOutput).setSequenceNumber(TransactionInput.NO_SEQUENCE - 1L);
    refundTx.setLockTime(expiryTime);
    if (Context.get().isEnsureMinRequiredFee()) {
        // Must pay min fee.
        final Coin valueAfterFee = totalValue.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
        if (Transaction.MIN_NONDUST_OUTPUT.compareTo(valueAfterFee) > 0)
            throw new ValueOutOfRangeException("totalValue too small to use");
        refundTx.addOutput(valueAfterFee, myKey.toAddress(params));
        refundFees = multisigFee.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
    } else {
        refundTx.addOutput(totalValue, myKey.toAddress(params));
        refundFees = multisigFee;
    }
    refundTx.getConfidence().setSource(TransactionConfidence.Source.SELF);
    log.info("initiated channel with multi-sig contract {}, refund {}", multisigContract.getHashAsString(),
            refundTx.getHashAsString());
    stateMachine.transition(State.INITIATED);
    // Client should now call getIncompleteRefundTransaction() and send it to the server.
}