Java Code Examples for org.bitcoinj.core.Transaction#getOutputs()

The following examples show how to use org.bitcoinj.core.Transaction#getOutputs() . 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: WalletService.java    From bisq-core with GNU Affero General Public License v3.0 6 votes vote down vote up
protected List<TransactionOutput> getOutputsWithConnectedOutputs(Transaction tx) {
    List<TransactionOutput> transactionOutputs = tx.getOutputs();
    List<TransactionOutput> connectedOutputs = new ArrayList<>();

    // add all connected outputs from any inputs as well
    List<TransactionInput> transactionInputs = tx.getInputs();
    for (TransactionInput transactionInput : transactionInputs) {
        TransactionOutput transactionOutput = transactionInput.getConnectedOutput();
        if (transactionOutput != null) {
            connectedOutputs.add(transactionOutput);
        }
    }

    List<TransactionOutput> mergedOutputs = new ArrayList<>();
    mergedOutputs.addAll(transactionOutputs);
    mergedOutputs.addAll(connectedOutputs);
    return mergedOutputs;
}
 
Example 2
Source File: SendRequest.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Construct a SendRequest for a CPFP (child-pays-for-parent) transaction. The resulting transaction is already
 * completed, so you should directly proceed to signing and broadcasting/committing the transaction. CPFP is
 * currently only supported by a few miners, so use with care.
 */
public static SendRequest childPaysForParent(Wallet wallet, Transaction parentTransaction, Coin feeRaise) {
    TransactionOutput outputToSpend = null;
    for (final TransactionOutput output : parentTransaction.getOutputs()) {
        if (output.isMine(wallet) && output.isAvailableForSpending()
                && output.getValue().isGreaterThan(feeRaise)) {
            outputToSpend = output;
            break;
        }
    }
    // TODO spend another confirmed output of own wallet if needed
    checkNotNull(outputToSpend, "Can't find adequately sized output that spends to us");

    final Transaction tx = new Transaction(parentTransaction.getParams());
    tx.addInput(outputToSpend);
    tx.addOutput(outputToSpend.getValue().subtract(feeRaise), wallet.freshAddress(KeyPurpose.CHANGE));
    tx.setPurpose(Transaction.Purpose.RAISE_FEE);
    final SendRequest req = forTx(tx);
    req.completed = true;
    return req;
}
 
Example 3
Source File: SendRequest.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Construct a SendRequest for a CPFP (child-pays-for-parent) transaction. The resulting transaction is already
 * completed, so you should directly proceed to signing and broadcasting/committing the transaction. CPFP is
 * currently only supported by a few miners, so use with care.
 */
public static SendRequest childPaysForParent(Wallet wallet, Transaction parentTransaction, Coin feeRaise) {
    TransactionOutput outputToSpend = null;
    for (final TransactionOutput output : parentTransaction.getOutputs()) {
        if (output.isMine(wallet) && output.isAvailableForSpending()
                && output.getValue().isGreaterThan(feeRaise)) {
            outputToSpend = output;
            break;
        }
    }
    // TODO spend another confirmed output of own wallet if needed
    checkNotNull(outputToSpend, "Can't find adequately sized output that spends to us");

    final Transaction tx = new Transaction(parentTransaction.getParams());
    tx.addInput(outputToSpend);
    tx.addOutput(outputToSpend.getValue().subtract(feeRaise), wallet.freshAddress(KeyPurpose.CHANGE));
    tx.setPurpose(Transaction.Purpose.RAISE_FEE);
    final SendRequest req = forTx(tx);
    req.completed = true;
    return req;
}
 
Example 4
Source File: ToolsTest.java    From thunder with GNU Affero General Public License v3.0 6 votes vote down vote up
private static Transaction shuffleTransaction (Transaction transaction) {
    Transaction shuffledTransaction = new Transaction(Constants.getNetwork());

    List<TransactionInput> shuffledInputs = new ArrayList<>(transaction.getInputs());
    List<TransactionOutput> shuffledOutputs = new ArrayList<>(transaction.getOutputs());

    Collections.shuffle(shuffledInputs);
    Collections.shuffle(shuffledOutputs);
    for (TransactionInput input : shuffledInputs) {
        shuffledTransaction.addInput(input);
    }
    for (TransactionOutput output : shuffledOutputs) {
        shuffledTransaction.addOutput(output);
    }
    return shuffledTransaction;
}
 
Example 5
Source File: SendRequest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Construct a SendRequest for a CPFP (child-pays-for-parent) transaction. The resulting transaction is already
 * completed, so you should directly proceed to signing and broadcasting/committing the transaction. CPFP is
 * currently only supported by a few miners, so use with care.
 */
public static SendRequest childPaysForParent(Wallet wallet, Transaction parentTransaction, Coin feeRaise) {
    TransactionOutput outputToSpend = null;
    for (final TransactionOutput output : parentTransaction.getOutputs()) {
        if (output.isMine(wallet) && output.isAvailableForSpending()
                && output.getValue().isGreaterThan(feeRaise)) {
            outputToSpend = output;
            break;
        }
    }
    // TODO spend another confirmed output of own wallet if needed
    checkNotNull(outputToSpend, "Can't find adequately sized output that spends to us");

    final Transaction tx = new Transaction(parentTransaction.getParams());
    tx.addInput(outputToSpend);
    tx.addOutput(outputToSpend.getValue().subtract(feeRaise), wallet.freshAddress(KeyPurpose.CHANGE));
    tx.setPurpose(Transaction.Purpose.RAISE_FEE);
    final SendRequest req = forTx(tx);
    req.completed = true;
    return req;
}
 
Example 6
Source File: RawTransactionInfo.java    From consensusj with Apache License 2.0 6 votes vote down vote up
/**
 * Construct from a bitcoinj transaction
 * @param transaction A bitcoinj confirmed or unconfirmed transaction
 */
public RawTransactionInfo(Transaction transaction) {
    this.hex = HexUtil.bytesToHexString(transaction.bitcoinSerialize());
    this.txid = transaction.getTxId();
    this.version = transaction.getVersion();
    this.locktime = transaction.getLockTime();
    this.blockhash = null;  // For now
    this.confirmations = transaction.getConfidence().getDepthInBlocks();
    this.time = 0; // TODO: block header time of block including transaction
    this.blocktime = this.time; // same as time (see API doc)
    vin = new VinList();
    for (TransactionInput input : transaction.getInputs()) {
        vin.add(new Vin(txid,
                        input.getOutpoint().getIndex(),
                        input.getScriptSig().toString(),
                        input.getSequenceNumber()));
    }
    vout = new VoutList();
    for (TransactionOutput output : transaction.getOutputs()) {
        vout.add(new Vout(output.getValue(),
                            output.getIndex(),
                            output.getScriptPubKey().toString()));
    }
}
 
Example 7
Source File: WalletService.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
protected List<TransactionOutput> getOutputsWithConnectedOutputs(Transaction tx) {
    List<TransactionOutput> transactionOutputs = tx.getOutputs();
    List<TransactionOutput> connectedOutputs = new ArrayList<>();

    // add all connected outputs from any inputs as well
    List<TransactionInput> transactionInputs = tx.getInputs();
    for (TransactionInput transactionInput : transactionInputs) {
        TransactionOutput transactionOutput = transactionInput.getConnectedOutput();
        if (transactionOutput != null) {
            connectedOutputs.add(transactionOutput);
        }
    }

    List<TransactionOutput> mergedOutputs = new ArrayList<>();
    mergedOutputs.addAll(transactionOutputs);
    mergedOutputs.addAll(connectedOutputs);
    return mergedOutputs;
}
 
Example 8
Source File: BsqWalletService.java    From bisq with GNU Affero General Public License v3.0 6 votes vote down vote up
public Transaction signTx(Transaction tx) throws WalletException, TransactionVerificationException {
    for (int i = 0; i < tx.getInputs().size(); i++) {
        TransactionInput txIn = tx.getInputs().get(i);
        TransactionOutput connectedOutput = txIn.getConnectedOutput();
        if (connectedOutput != null && connectedOutput.isMine(wallet)) {
            signTransactionInput(wallet, aesKey, tx, txIn, i);
            checkScriptSig(tx, txIn, i);
        }
    }

    for (TransactionOutput txo : tx.getOutputs()) {
        Coin value = txo.getValue();
        // OpReturn outputs have value 0
        if (value.isPositive()) {
            checkArgument(Restrictions.isAboveDust(txo.getValue()),
                    "An output value is below dust limit. Transaction=" + tx);
        }
    }

    checkWalletConsistency(wallet);
    verifyTransaction(tx);
    printTx("BSQ wallet: Signed Tx", tx);
    return tx;
}
 
Example 9
Source File: TransactionSummary.java    From jelectrum with MIT License 5 votes vote down vote up
public TransactionSummary(Transaction tx, TXUtil tx_util, boolean confirmed, Map<Sha256Hash, Transaction> block_tx_map)
{
  tx_hash = tx.getHash();
  involved_scripthashes = new HashSet<>();
  ins = new TreeMap<>();
  outs = new TreeMap<>();

  long total_out=0L;
  long total_in=0L;

  for(TransactionOutput tx_out : tx.getOutputs())
  {
    TransactionOutSummary os = new TransactionOutSummary(tx_out, tx_util);

    outs.put( tx_out.getIndex(), os );
    involved_scripthashes.add(os.getScriptHash());
    total_out += os.getValue();
  }
  int idx=0;
  for(TransactionInput tx_in : tx.getInputs())
  {
    TransactionInSummary is = new TransactionInSummary(tx_in, tx_util, confirmed, block_tx_map);

    ByteString addr = is.getScriptHash();
    if (addr != null) involved_scripthashes.add(addr);

    ins.put(idx, is);  
    idx++;
    total_in += is.getValue();
  }
  if (tx.isCoinBase())
  {
    fee = 0L;
  }
  else
  {
    fee = total_in - total_out;
  }
  size = tx.getOptimalEncodingMessageSize();
}
 
Example 10
Source File: WithdrawalView.java    From bisq with GNU Affero General Public License v3.0 5 votes vote down vote up
private Coin getDust(Transaction transaction) {
    Coin dust = Coin.ZERO;
    for (TransactionOutput transactionOutput: transaction.getOutputs()) {
        if (transactionOutput.getValue().isLessThan(Restrictions.getMinNonDustOutput())) {
            dust = dust.add(transactionOutput.getValue());
            log.info("dust TXO = {}", transactionOutput.toString());
        }
    }
    return dust;
}
 
Example 11
Source File: DumpTx.java    From jelectrum with MIT License 4 votes vote down vote up
public static boolean hasStrangeData(Transaction tx)
{
  try
  {
  boolean hasStrange = false;
  /*for(TransactionInput in : tx.getInputs())
  {
    Script script = in.getScriptSig();
    int data_in = 0;
     for(ScriptChunk chunk : script.getChunks())
    {
      if (chunk.isOpCode())
      {
      }
      if (chunk.isPushData() && (chunk.data != null))
      {
        data_in += chunk.data.length;
      }

    }
    if (data_in > 20) return true;

  }*/
  int extra_data = 0;

  for(TransactionOutput out : tx.getOutputs())
  {
    int data_out = 0;
    Script script = out.getScriptPubKey();

    for(ScriptChunk chunk : script.getChunks())
    {
      if (chunk.isOpCode())
      {
      }
      if (chunk.isPushData() && (chunk.data != null))
      {
        data_out += chunk.data.length;
      }

    }
    if (data_out > 20) extra_data += data_out;


  }

  if (extra_data > 20) return true;

  return false;
  }

  catch(Throwable t){return true;}


}
 
Example 12
Source File: UtxoTrieMgr.java    From jelectrum with MIT License 4 votes vote down vote up
public static void main(String args[]) throws Exception
{
  String config_path = args[0];
  Jelectrum jelly = new Jelectrum(new Config(config_path));

  int block_number = Integer.parseInt(args[1]);
  
  Sha256Hash block_hash = jelly.getBlockChainCache().getBlockHashAtHeight(block_number);
  System.out.println("Block hash: " + block_hash);
  Block b = jelly.getDB().getBlock(block_hash).getBlock(jelly.getNetworkParameters());
  System.out.println("Inspecting " + block_number + " - " + block_hash);

  int tx_count =0;
  int out_count =0;
  for(Transaction tx : b.getTransactions())
  {
    int idx=0;
    for(TransactionOutput tx_out : tx.getOutputs())
    {
      byte[] pub_key_bytes=getPublicKeyForTxOut(tx_out, jelly.getNetworkParameters());

      String public_key = null;
      if (pub_key_bytes != null) public_key = Hex.encodeHexString(pub_key_bytes);
      else public_key = "None";
      
      String script_bytes = Hex.encodeHexString(tx_out.getScriptBytes());

      String[] cmd=new String[3];
      cmd[0]="python";
      cmd[1]=jelly.getConfig().get("utxo_check_tool");
      cmd[2]=script_bytes;

      //System.out.println(script_bytes);

      Process p = Runtime.getRuntime().exec(cmd);

      Scanner scan = new Scanner(p.getInputStream());
      String ele_key = scan.nextLine();

      if (!ele_key.equals(public_key))
      {
        System.out.println("Mismatch on " + tx_out.getParentTransaction().getHash() + ":" + idx);
        System.out.println("  Script: " + script_bytes);
        System.out.println("  Jelectrum: " + public_key);
        System.out.println("  Electrum:  " + ele_key);

      }

      out_count++;
      idx++;
    }
    tx_count++;
  }
  System.out.println("TX Count: " + tx_count);
  System.out.println("Out Count: " + out_count);


}
 
Example 13
Source File: DumpTxList.java    From jelectrum with MIT License 4 votes vote down vote up
public static boolean hasStrangeData(Transaction tx)
{
  try
  {
  boolean hasStrange = false;
  /*for(TransactionInput in : tx.getInputs())
  {
    Script script = in.getScriptSig();
    int data_in = 0;
     for(ScriptChunk chunk : script.getChunks())
    {
      if (chunk.isOpCode())
      {
      }
      if (chunk.isPushData() && (chunk.data != null))
      {
        data_in += chunk.data.length;
      }

    }
    if (data_in > 20) return true;

  }*/
  int extra_data = 0;

  for(TransactionOutput out : tx.getOutputs())
  {
    int data_out = 0;
    Script script = out.getScriptPubKey();

    for(ScriptChunk chunk : script.getChunks())
    {
      if (chunk.isOpCode())
      {
      }
      if (chunk.isPushData() && (chunk.data != null))
      {
        data_out += chunk.data.length;
      }

    }
    if (data_out > 20) extra_data += data_out;


  }

  if (extra_data > 20) return true;

  return false;
  }

  catch(Throwable t){return true;}


}
 
Example 14
Source File: DumpTxList.java    From jelectrum with MIT License 4 votes vote down vote up
public static void main(String args[]) throws Exception
{
  Jelectrum jelly = new Jelectrum(new Config(args[0]));

  Scanner scan = new Scanner(new FileInputStream(args[1]));

  PrintStream pout = new PrintStream(new FileOutputStream(args[2], false));

  TXUtil txutil = new TXUtil(jelly.getDB(), jelly.getNetworkParameters());

  while(scan.hasNext())
  {
    String hash = scan.next();
    Transaction tx = jelly.getDB().getTransaction(new Sha256Hash(hash)).getTx(jelly.getNetworkParameters());


    int in_idx =0;
    for(TransactionInput in : tx.getInputs())
    {
      Address addr = in.getFromAddress();

      byte[] h160 = addr.getHash160();

      pout.println("txin:" + hash + ":" + in_idx + ":" + Hex.encodeHexString(h160));

      in_idx++;

      /*System.out.println("Input: " + in);
      Script script = in.getScriptSig();
      for(ScriptChunk chunk : script.getChunks())
      {
        if (chunk.isOpCode())
        {
          System.out.println("    op " + chunk.opcode);
        }
        if (chunk.isPushData() && (chunk.data != null))
        {
          System.out.println("    data " + chunk.data.length);
        }

      }*/
    }
    pout.println("tx:" + hash + ":" + txutil.getTXBlockHeight(tx, jelly.getBlockChainCache()));

  for(TransactionOutput out : tx.getOutputs())
  {
    int idx = out.getIndex();
    Script script = out.getScriptPubKey();

    for(ScriptChunk chunk : script.getChunks())
    {
      if (chunk.isOpCode())
      {
        //System.out.println("    op " + chunk.opcode);
      }
      if (chunk.isPushData() && (chunk.data != null))
      {
        pout.println("txout:" + hash + ":" + idx + ":" + Hex.encodeHexString(chunk.data));
      }

    }


  }

  }

  pout.flush();
  pout.close();



}
 
Example 15
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 16
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 17
Source File: UnconfirmedBsqChangeOutputListService.java    From bisq with GNU Affero General Public License v3.0 4 votes vote down vote up
/**
 * Once a tx gets committed to our BSQ wallet we store the change output for allowing it to be spent in follow-up
 * transactions.
 */
public void onCommitTx(Transaction tx, TxType txType, Wallet wallet) {
    // We remove all potential connected outputs from our inputs as they would have been spent.
    removeConnectedOutputsOfInputsOfTx(tx);

    int changeOutputIndex;
    switch (txType) {
        case UNDEFINED_TX_TYPE:
        case UNVERIFIED:
        case INVALID:
        case GENESIS:
            return;
        case TRANSFER_BSQ:
            changeOutputIndex = 1; // output 0 is receiver's address
            break;
        case PAY_TRADE_FEE:
            changeOutputIndex = 0;
            break;
        case PROPOSAL:
            changeOutputIndex = 0;
            break;
        case COMPENSATION_REQUEST:
        case REIMBURSEMENT_REQUEST:
            changeOutputIndex = 0;
            break;
        case BLIND_VOTE:
            changeOutputIndex = 1; // output 0 is stake
            break;
        case VOTE_REVEAL:
            changeOutputIndex = 0;
            break;
        case LOCKUP:
            changeOutputIndex = 1; // output 0 is lockup amount
            break;
        case UNLOCK:
            // We don't allow to spend the unlocking funds as there is the lock time which need to pass,
            // otherwise the funds get burned!
            return;
        case ASSET_LISTING_FEE:
            changeOutputIndex = 0;
            break;
        case PROOF_OF_BURN:
            changeOutputIndex = 0;
            break;
        case IRREGULAR:
            return;
        default:
            return;
    }

    // It can be that we don't have a BSQ and a BTC change output.
    // If no BSQ change but a BTC change the index points to the BTC output and then
    // we detect that it is not part of our wallet.
    // If there is a BSQ change but no BTC change it has no effect as we ignore BTC outputs anyway.
    // If both change outputs do not exist then we might point to an index outside
    // of the list and we return at our scope check.

    // If no BTC output (unlikely but
    // possible) the index points to the BTC output and then we detect that it is not part of our wallet.
    //
    List<TransactionOutput> outputs = tx.getOutputs();
    if (changeOutputIndex > outputs.size() - 1)
        return;

    TransactionOutput change = outputs.get(changeOutputIndex);
    if (!change.isMine(wallet))
        return;

    UnconfirmedTxOutput txOutput = UnconfirmedTxOutput.fromTransactionOutput(change);
    if (unconfirmedBsqChangeOutputList.containsTxOutput(txOutput))
        return;

    unconfirmedBsqChangeOutputList.add(txOutput);
    persist();
}
 
Example 18
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;
}
 
Example 19
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;
}