use of org.bitcoinj.core.Address in project bisq-core by bisq-network.
the class BtcWalletService method completePreparedCompensationRequestTx.
// /////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
// /////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////
// CompensationRequest tx
// /////////////////////////////////////////////////////////////////////////////////////////
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 (>= 2730 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 [0-1] BSQ request fee change output (>= 2730 Satoshi)
// outputs [1] Potentially BSQ issuance output (>= 2730 Satoshi)
// outputs [0-1] BTC change output from issuance and miner fee inputs (>= 2730 Satoshi)
// outputs [0-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 = getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).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
for (int i = indexOfBtcFirstInput; 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.");
signTransactionInput(wallet, aesKey, resultTx, txIn, i);
checkScriptSig(resultTx, txIn, i);
}
checkWalletConsistency(wallet);
verifyTransaction(resultTx);
printTx("BTC wallet: Signed tx", resultTx);
return resultTx;
}
use of org.bitcoinj.core.Address in project bisq-core by bisq-network.
the class BtcWalletService method completePreparedVoteRevealTx.
// /////////////////////////////////////////////////////////////////////////////////////////
// MyVote reveal tx
// /////////////////////////////////////////////////////////////////////////////////////////
// TODO is same as blind vote tx
public Transaction completePreparedVoteRevealTx(Transaction feeTx, byte[] opReturnData) throws TransactionVerificationException, WalletException, InsufficientMoneyException {
// (BsqFee)tx has following structure:
// inputs [1] BSQ inputs (stake)
// inputs [1-n] BSQ inputs (fee)
// outputs [1] BSQ unlocked stake
// outputs [0-1] BSQ change output (>= 2730 Satoshi)
// preparedVoteTx has following structure:
// inputs [1] BSQ inputs (stake)
// inputs [1-n] BSQ inputs (fee)
// inputs [1-n] BTC inputs for miner fee
// outputs [1] BSQ unlocked stake
// outputs [0-1] BSQ change output (>= 2730 Satoshi)
// outputs [0-1] BTC change output from miner fee inputs (>= 2730 Satoshi)
// outputs [0-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();
// BSQ change outputs from BSQ fee inputs.
feeTx.getOutputs().forEach(preparedTx::addOutput);
// 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 = getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).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);
// Sign all BTC inputs
for (int i = indexOfBtcFirstInput; 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.");
signTransactionInput(wallet, aesKey, resultTx, txIn, i);
checkScriptSig(resultTx, txIn, i);
}
checkWalletConsistency(wallet);
verifyTransaction(resultTx);
printTx("BTC wallet: Signed tx", resultTx);
return resultTx;
}
use of org.bitcoinj.core.Address in project bisq-core by bisq-network.
the class BtcWalletService method doubleSpendTransaction.
// /////////////////////////////////////////////////////////////////////////////////////////
// Double spend unconfirmed transaction (unlock in case we got into a tx with a too low mining fee)
// /////////////////////////////////////////////////////////////////////////////////////////
public void doubleSpendTransaction(String txId, Runnable resultHandler, ErrorMessageHandler errorMessageHandler) throws InsufficientFundsException {
AddressEntry addressEntry = getOrCreateUnusedAddressEntry(AddressEntry.Context.AVAILABLE);
checkNotNull(addressEntry.getAddress(), "addressEntry.getAddress() must not be null");
Optional<Transaction> transactionOptional = wallet.getTransactions(true).stream().filter(t -> t.getHashAsString().equals(txId)).findAny();
if (transactionOptional.isPresent()) {
Transaction txToDoubleSpend = transactionOptional.get();
Address toAddress = addressEntry.getAddress();
final TransactionConfidence.ConfidenceType confidenceType = txToDoubleSpend.getConfidence().getConfidenceType();
if (confidenceType == TransactionConfidence.ConfidenceType.PENDING) {
log.debug("txToDoubleSpend no. of inputs " + txToDoubleSpend.getInputs().size());
Transaction newTransaction = new Transaction(params);
txToDoubleSpend.getInputs().stream().forEach(input -> {
final TransactionOutput connectedOutput = input.getConnectedOutput();
if (connectedOutput != null && connectedOutput.isMine(wallet) && connectedOutput.getParentTransaction() != null && connectedOutput.getParentTransaction().getConfidence() != null && input.getValue() != null) {
// if (connectedOutput.getParentTransaction().getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
newTransaction.addInput(new TransactionInput(params, newTransaction, new byte[] {}, new TransactionOutPoint(params, input.getOutpoint().getIndex(), new Transaction(params, connectedOutput.getParentTransaction().bitcoinSerialize())), Coin.valueOf(input.getValue().value)));
/* } else {
log.warn("Confidence of parent tx is not of type BUILDING: ConfidenceType=" +
connectedOutput.getParentTransaction().getConfidence().getConfidenceType());
}*/
}
});
log.info("newTransaction no. of inputs " + newTransaction.getInputs().size());
log.info("newTransaction size in kB " + newTransaction.bitcoinSerialize().length / 1024);
if (!newTransaction.getInputs().isEmpty()) {
Coin amount = Coin.valueOf(newTransaction.getInputs().stream().mapToLong(input -> input.getValue() != null ? input.getValue().value : 0).sum());
newTransaction.addOutput(amount, toAddress);
try {
Coin fee;
int counter = 0;
int txSize = 0;
Transaction tx;
SendRequest sendRequest;
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
do {
counter++;
fee = txFeeForWithdrawalPerByte.multiply(txSize);
newTransaction.clearOutputs();
newTransaction.addOutput(amount.subtract(fee), toAddress);
sendRequest = SendRequest.forTx(newTransaction);
sendRequest.fee = fee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey;
sendRequest.coinSelector = new BtcCoinSelector(toAddress);
sendRequest.changeAddress = toAddress;
wallet.completeTx(sendRequest);
tx = sendRequest.tx;
txSize = tx.bitcoinSerialize().length;
printTx("FeeEstimationTransaction", tx);
sendRequest.tx.getOutputs().forEach(o -> log.debug("Output value " + o.getValue().toFriendlyString()));
} while (feeEstimationNotSatisfied(counter, tx));
if (counter == 10)
log.error("Could not calculate the fee. Tx=" + tx);
Wallet.SendResult sendResult = null;
try {
sendRequest = SendRequest.forTx(newTransaction);
sendRequest.fee = fee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey;
sendRequest.coinSelector = new BtcCoinSelector(toAddress);
sendRequest.changeAddress = toAddress;
sendResult = wallet.sendCoins(sendRequest);
} catch (InsufficientMoneyException e) {
// in some cases getFee did not calculate correctly and we still get an InsufficientMoneyException
log.warn("We still have a missing fee " + (e.missing != null ? e.missing.toFriendlyString() : ""));
amount = amount.subtract(e.missing);
newTransaction.clearOutputs();
newTransaction.addOutput(amount, toAddress);
sendRequest = SendRequest.forTx(newTransaction);
sendRequest.fee = fee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey;
sendRequest.coinSelector = new BtcCoinSelector(toAddress, false);
sendRequest.changeAddress = toAddress;
try {
sendResult = wallet.sendCoins(sendRequest);
printTx("FeeEstimationTransaction", newTransaction);
} catch (InsufficientMoneyException e2) {
errorMessageHandler.handleErrorMessage("We did not get the correct fee calculated. " + (e2.missing != null ? e2.missing.toFriendlyString() : ""));
}
}
if (sendResult != null) {
log.info("Broadcasting double spending transaction. " + sendResult.tx);
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction result) {
log.info("Double spending transaction published. " + result);
resultHandler.run();
}
@Override
public void onFailure(@NotNull Throwable t) {
log.error("Broadcasting double spending transaction failed. " + t.getMessage());
errorMessageHandler.handleErrorMessage(t.getMessage());
}
});
}
} 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"));
}
} else {
String errorMessage = "We could not find inputs we control in the transaction we want to double spend.";
log.warn(errorMessage);
errorMessageHandler.handleErrorMessage(errorMessage);
}
} else if (confidenceType == TransactionConfidence.ConfidenceType.BUILDING) {
errorMessageHandler.handleErrorMessage("That transaction is already in the blockchain so we cannot double spend it.");
} else if (confidenceType == TransactionConfidence.ConfidenceType.DEAD) {
errorMessageHandler.handleErrorMessage("One of the inputs of that transaction has been already double spent.");
}
}
}
use of org.bitcoinj.core.Address in project bisq-core by bisq-network.
the class BtcWalletService method completePreparedBsqTx.
public Transaction completePreparedBsqTx(Transaction preparedBsqTx, boolean useCustomTxFee, @Nullable byte[] opReturnData) throws TransactionVerificationException, WalletException, InsufficientMoneyException {
// preparedBsqTx has following structure:
// inputs [1-n] BSQ inputs
// outputs [0-1] BSQ receivers output
// outputs [0-1] BSQ change output
// mining fee: optional burned BSQ fee (only if opReturnData != null)
// We add BTC mining fee. Result tx looks like:
// inputs [1-n] BSQ inputs
// inputs [1-n] BTC inputs
// outputs [0-1] BSQ receivers output
// outputs [0-1] BSQ change output
// outputs [0-1] BTC change output
// outputs [0-1] OP_RETURN with opReturnData (only if opReturnData != null)
// mining fee: BTC mining fee + optional burned BSQ fee (only if opReturnData != null)
// 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.
// 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 2 inputs
int txSizeWithUnsignedInputs = 203;
// If useCustomTxFee we allow overriding the estimated fee from preferences
final Coin txFeePerByte = useCustomTxFee ? getTxFeeForWithdrawalPerByte() : feeService.getTxFeePerByte();
// In case there are no change outputs we force a change by adding min dust to the BTC input
Coin forcedChangeValue = Coin.ZERO;
Address changeAddress = getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress();
checkNotNull(changeAddress, "changeAddress must not be null");
final BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
final List<TransactionInput> preparedBsqTxInputs = preparedBsqTx.getInputs();
final List<TransactionOutput> preparedBsqTxOutputs = preparedBsqTx.getOutputs();
// We add 1 for the BTC fee input
int numInputs = preparedBsqTxInputs.size() + 1;
Transaction resultTx = null;
boolean isFeeOutsideTolerance;
boolean opReturnIsOnlyOutput;
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);
if (forcedChangeValue.isZero()) {
preparedBsqTxOutputs.stream().forEach(tx::addOutput);
} else {
// TODO test that case
checkArgument(preparedBsqTxOutputs.size() == 0, "preparedBsqTxOutputs.size must be null in that code branch");
tx.addOutput(forcedChangeValue, changeAddress);
}
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;
// We might have the rare case that both inputs matched the required fees, so both did not require
// a change output.
// In such cases we need to add artificially a change output (OP_RETURN is not allowed as only output)
opReturnIsOnlyOutput = resultTx.getOutputs().size() == 0;
forcedChangeValue = opReturnIsOnlyOutput ? Restrictions.getMinNonDustOutput() : Coin.ZERO;
// add OP_RETURN output
if (opReturnData != null)
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 (opReturnIsOnlyOutput || isFeeOutsideTolerance || resultTx.getFee().value < txFeePerByte.multiply(resultTx.bitcoinSerialize().length).value);
// Sign all BTC inputs
for (int i = preparedBsqTxInputs.size(); 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.");
signTransactionInput(wallet, aesKey, resultTx, txIn, i);
checkScriptSig(resultTx, txIn, i);
}
checkWalletConsistency(wallet);
verifyTransaction(resultTx);
printTx("BTC wallet: Signed tx", resultTx);
return resultTx;
}
use of org.bitcoinj.core.Address in project bisq-core by bisq-network.
the class TradeWalletService method takerCreatesDepositsTxInputs.
// /////////////////////////////////////////////////////////////////////////////////////////
// Trade
// /////////////////////////////////////////////////////////////////////////////////////////
// We construct the deposit transaction in the way that the buyer is always the first entry (inputs, outputs, MS keys) and then the seller.
// In the creation of the deposit tx the taker/maker roles are the determining roles instead of buyer/seller.
// In the payout tx is is the buyer/seller role. We keep the buyer/seller ordering over all transactions to not get confusion with ordering,
// which is important to follow correctly specially for the order of the MS keys.
/**
* The taker creates a dummy transaction to get the input(s) and optional change output for the amount and the takersAddress for that trade.
* That will be used to send to the maker for creating the deposit transaction.
*
* @param inputAmount Amount of takers input
* @param txFee Mining fee
* @param takersAddress Address of taker
* @return A data container holding the inputs, the output value and address
* @throws TransactionVerificationException
* @throws WalletException
*/
public InputsAndChangeOutput takerCreatesDepositsTxInputs(Coin inputAmount, Coin txFee, Address takersAddress, Address takersChangeAddress) throws TransactionVerificationException, WalletException {
log.debug("takerCreatesDepositsTxInputs called");
log.debug("inputAmount " + inputAmount.toFriendlyString());
log.debug("txFee " + txFee.toFriendlyString());
log.debug("takersAddress " + takersAddress.toString());
// We add the mining fee 2 times to the deposit tx:
// 1. Will be spent when publishing the deposit tx (paid by buyer)
// 2. Will be added to the MS amount, so when publishing the payout tx the fee is already there and the outputs are not changed by fee reduction
// The fee for the payout will be paid by the seller.
/*
The tx we create has that structure:
IN[0] any input > inputAmount (including tx fee) (unsigned)
IN[1...n] optional inputs supported, but normally there is just 1 input (unsigned)
OUT[0] dummyOutputAmount (inputAmount - tx fee)
OUT[1] Optional Change = inputAmount - dummyOutputAmount - tx fee
We are only interested in the inputs and the optional change output.
*/
// inputAmount includes the tx fee. So we subtract the fee to get the dummyOutputAmount.
Coin dummyOutputAmount = inputAmount.subtract(txFee);
Transaction dummyTX = new Transaction(params);
// The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything.
// We don't care about fee calculation differences between the real tx and that dummy tx as we use a static tx fee.
TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, new ECKey().toAddress(params));
dummyTX.addOutput(dummyOutput);
// Find the needed inputs to pay the output, optionally add 1 change output.
// Normally only 1 input and no change output is used, but we support multiple inputs and 1 change output.
// Our spending transaction output is from the create offer fee payment.
addAvailableInputsAndChangeOutputs(dummyTX, takersAddress, takersChangeAddress, txFee);
// The completeTx() call signs the input, but we don't want to pass over signed tx inputs so we remove the signature
WalletService.removeSignatures(dummyTX);
WalletService.verifyTransaction(dummyTX);
// WalletService.printTx("dummyTX", dummyTX);
List<RawTransactionInput> rawTransactionInputList = dummyTX.getInputs().stream().map(e -> {
checkNotNull(e.getConnectedOutput(), "e.getConnectedOutput() must not be null");
checkNotNull(e.getConnectedOutput().getParentTransaction(), "e.getConnectedOutput().getParentTransaction() must not be null");
checkNotNull(e.getValue(), "e.getValue() must not be null");
return getRawInputFromTransactionInput(e);
}).collect(Collectors.toList());
// We don't support more then 1 change outputs, so there are max. 2 outputs
checkArgument(dummyTX.getOutputs().size() < 3);
// Only interested in optional change output, the dummy output at index 0 is ignored (that's why we use index 1)
TransactionOutput changeOutput = dummyTX.getOutputs().size() == 2 ? dummyTX.getOutputs().get(1) : null;
long changeOutputValue = 0L;
String changeOutputAddress = null;
if (changeOutput != null) {
changeOutputValue = changeOutput.getValue().getValue();
Address addressFromP2PKHScript = changeOutput.getAddressFromP2PKHScript(params);
checkNotNull(addressFromP2PKHScript, "changeOutput.getAddressFromP2PKHScript(params) must not be null");
changeOutputAddress = addressFromP2PKHScript.toString();
}
return new InputsAndChangeOutput(new ArrayList<>(rawTransactionInputList), changeOutputValue, changeOutputAddress);
}
Aggregations