Search in sources :

Example 16 with TransactionInput

use of org.bitcoinj.core.TransactionInput in project catena-java by alinush.

the class TxUtils method findDoubleSpendsAgainst.

/**
 * Checks if any of the transactions in txs spend the same outputs as the transactions in candidates.
 *
 * The ordering of the transactions in the collection associated with the output is the following.
 * (1) first, the tx from 'txs'
 * (2) second, any double spends from 'candidates'
 *
 * @param txs
 * @param candidates
 * @return
 */
public static Map<TransactionOutPoint, List<Transaction>> findDoubleSpendsAgainst(Iterator<Transaction> txsIt, Map<Sha256Hash, Transaction> candidates) {
    // Maps an outpoint to a list of transactions that spend it (ideally, that list should be of size one)
    ArrayListMultimap<TransactionOutPoint, Transaction> outpoints = ArrayListMultimap.create();
    while (txsIt.hasNext()) {
        Transaction tx = txsIt.next();
        // Coinbase TXs cannot double spend
        if (tx.isCoinBase())
            continue;
        // Compile a set of outpoints that are spent by tx.
        for (TransactionInput input : tx.getInputs()) {
            outpoints.put(input.getOutpoint(), tx);
        }
    }
    // Now for each candidate transaction, see if it spends any outpoints as this tx.
    for (Transaction tx : candidates.values()) {
        for (TransactionInput input : tx.getInputs()) {
            TransactionOutPoint outpoint = input.getOutpoint();
            // double spends amongst txs in candidates, which might not be desired by callers.
            if (outpoints.containsKey(outpoint)) {
                // It does, it's a double spend against the candidates, which makes it relevant.
                outpoints.put(outpoint, tx);
            }
        }
    }
    // Now clear <outp, list(tx)> pairs where the sizeof(list) == 1 (i.e., no double spend)
    Map<TransactionOutPoint, List<Transaction>> doubleSpends = Multimaps.asMap(outpoints);
    Iterator<List<Transaction>> it = doubleSpends.values().iterator();
    while (it.hasNext()) {
        int numSpends = it.next().size();
        if (numSpends < 2) {
            it.remove();
        }
    }
    return doubleSpends;
}
Also used : Transaction(org.bitcoinj.core.Transaction) List(java.util.List) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint)

Example 17 with TransactionInput

use of org.bitcoinj.core.TransactionInput in project catena-java by alinush.

the class TxUtils method findDoubleSpendsAmongst.

public static Map<TransactionOutPoint, List<Transaction>> findDoubleSpendsAmongst(Collection<Transaction> candidates) {
    ArrayListMultimap<TransactionOutPoint, Transaction> map = ArrayListMultimap.create();
    // "spends" <o, tx> pair to the map.
    for (Transaction tx : candidates) {
        for (TransactionInput input : tx.getInputs()) {
            TransactionOutPoint outpoint = input.getOutpoint();
            map.put(outpoint, tx);
        }
    }
    // Now clear <o, list(tx)> pairs where the sizeof(list) == 1 (i.e., no double spend)
    Map<TransactionOutPoint, List<Transaction>> doubleSpends = Multimaps.asMap(map);
    Iterator<List<Transaction>> it = doubleSpends.values().iterator();
    while (it.hasNext()) {
        int numSpends = it.next().size();
        if (numSpends < 2) {
            it.remove();
        }
    }
    return doubleSpends;
}
Also used : Transaction(org.bitcoinj.core.Transaction) List(java.util.List) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint)

Example 18 with TransactionInput

use of org.bitcoinj.core.TransactionInput in project catena-java by alinush.

the class CatenaUtils method isCatenaTxHelper.

/**
 * Returns true if the specified tx is a valid Catena TX created from the
 * UTXO in prevLink and signed by chainAddr.
 * If prevLink is null, then the previous UTXO is not checked.
 * If chainAddr is null, then the chain's address is not checked.
 *
 * @param tx
 * @param chainAddr
 * @param prevLink
 * @return
 */
private static boolean isCatenaTxHelper(Transaction tx, boolean verifySig, Address chainAddr, TransactionOutput prevLink, boolean checkPrevLinkIndex) {
    checkNotNull(tx);
    log.trace("Inspecting TX " + tx);
    String txid = tx.getHashAsString();
    NetworkParameters params = tx.getParams();
    // Check if this TX is properly connected to the previous TX's (unique) UTXO
    if (prevLink != null) {
        // Check the prev TX's output is #0
        if (checkPrevLinkIndex && prevLink.getIndex() != 0) {
            log.warn("Index of UTXO '" + prevLink.getOutPointFor() + "' was '" + prevLink.getIndex() + "' but expected index 0 (w.r.t. to txid=" + txid + ")");
            return false;
        }
        if (checkConnectedTo(tx, prevLink) == false)
            return false;
        // Check that the address in the previous output matches the one provided to this call
        Address prevLinkAddr = prevLink.getAddressFromP2PKHScript(tx.getParams());
        if (chainAddr != null && prevLinkAddr.equals(chainAddr) == false) {
            log.warn("Address in UTXO '" + prevLink.getOutPointFor() + "' was '" + prevLinkAddr + "' but expected chain address '" + chainAddr + "' (w.r.t. to txid=" + txid + ")");
            return false;
        }
        // Verify the signature on the first input
        if (verifySig) {
            TransactionInput firstInput = tx.getInput(0);
            try {
                firstInput.verify();
            } catch (ScriptException e) {
                log.warn("TX '" + txid + "' has invalid signature: " + Throwables.getStackTraceAsString(e));
                return false;
            } catch (VerificationException e) {
                log.warn("TX '" + txid + "' has invalid format: " + Throwables.getStackTraceAsString(e));
                return false;
            } catch (Throwable e) {
                log.warn("TX '" + txid + "' unknown signature verification error: " + Throwables.getStackTraceAsString(e));
                return false;
            }
        }
    }
    // Make sure we have only one input
    if (tx.getInputs().size() != 1) {
        log.warn("expected only one input in tx '" + txid + "', got " + tx.getInputs().size());
        return false;
    }
    // Make sure we have only two outputs (data + next)
    if (tx.getOutputs().size() != 2) {
        log.warn("expected two outputs in tx '" + txid + "' (continuation and OP_RETURN), got " + tx.getOutputs().size());
        return false;
    }
    // Make sure chain's address is correct in first output
    Address firstOutputAddr = tx.getOutput(0).getAddressFromP2PKHScript(params);
    if (chainAddr != null && !firstOutputAddr.equals(chainAddr)) {
        log.warn("first output address of '" + txid + "' was '" + firstOutputAddr + "'; expected chain address '" + chainAddr + "'");
        return false;
    }
    // Make sure 2nd output is an OP_RETURN
    Script secondOutput = tx.getOutput(1).getScriptPubKey();
    if (!secondOutput.isOpReturn()) {
        log.warn("second output of '" + txid + "' was supposed to be an OP_RETURN, got '" + secondOutput + "'");
        return false;
    }
    // All is well.
    return true;
}
Also used : ScriptException(org.bitcoinj.core.ScriptException) Script(org.bitcoinj.script.Script) Address(org.bitcoinj.core.Address) NetworkParameters(org.bitcoinj.core.NetworkParameters) VerificationException(org.bitcoinj.core.VerificationException) TransactionInput(org.bitcoinj.core.TransactionInput)

Example 19 with TransactionInput

use of org.bitcoinj.core.TransactionInput 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;
}
Also used : TransactionOutput(org.bitcoinj.core.TransactionOutput) SendRequest(org.bitcoinj.wallet.SendRequest) Address(org.bitcoinj.core.Address) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction)

Example 20 with TransactionInput

use of org.bitcoinj.core.TransactionInput 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;
}
Also used : TransactionOutput(org.bitcoinj.core.TransactionOutput) SendRequest(org.bitcoinj.wallet.SendRequest) Address(org.bitcoinj.core.Address) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction)

Aggregations

TransactionInput (org.bitcoinj.core.TransactionInput)36 Transaction (org.bitcoinj.core.Transaction)29 TransactionOutput (org.bitcoinj.core.TransactionOutput)20 TransactionOutPoint (org.bitcoinj.core.TransactionOutPoint)18 Script (org.bitcoinj.script.Script)16 ECKey (org.bitcoinj.core.ECKey)11 HashMap (java.util.HashMap)10 Address (org.bitcoinj.core.Address)9 Coin (org.bitcoinj.core.Coin)9 ArrayList (java.util.ArrayList)7 AddressFormatException (org.bitcoinj.core.AddressFormatException)7 RawTransactionInput (bisq.core.btc.data.RawTransactionInput)6 TransactionSignature (org.bitcoinj.crypto.TransactionSignature)6 ScriptException (org.bitcoinj.script.ScriptException)6 MyTransactionOutPoint (com.samourai.wallet.send.MyTransactionOutPoint)5 IOException (java.io.IOException)5 Sha256Hash (org.bitcoinj.core.Sha256Hash)5 MnemonicException (org.bitcoinj.crypto.MnemonicException)5 SendRequest (org.bitcoinj.wallet.SendRequest)5 BlockedUTXO (com.samourai.wallet.send.BlockedUTXO)4