Search in sources :

Example 1 with TxOutputKey

use of bisq.core.dao.state.blockchain.TxOutputKey in project bisq-core by bisq-network.

the class NonBsqCoinSelector method isTxOutputSpendable.

@Override
protected boolean isTxOutputSpendable(TransactionOutput output) {
    // output.getParentTransaction() cannot be null as it is checked in calling method
    Transaction parentTransaction = output.getParentTransaction();
    if (parentTransaction == null)
        return false;
    if (parentTransaction.getConfidence().getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING)
        return false;
    TxOutputKey key = new TxOutputKey(parentTransaction.getHashAsString(), output.getIndex());
    // It might be that we received BTC in a non-BSQ tx so that will not be stored in out state and not found.
    // So we consider any txOutput which is not in the state as BTC output.
    boolean outputIsNotInBsqState = !bsqStateService.existsTxOutput(key);
    return outputIsNotInBsqState || bsqStateService.getBtcTxOutput(key).isPresent();
}
Also used : Transaction(org.bitcoinj.core.Transaction) TxOutputKey(bisq.core.dao.state.blockchain.TxOutputKey)

Example 2 with TxOutputKey

use of bisq.core.dao.state.blockchain.TxOutputKey in project bisq-core by bisq-network.

the class BlockParserTest method testIsBsqTx.

// FIXME
@Test
public void testIsBsqTx() {
    // Setup a basic transaction with two inputs
    int height = 200;
    String hash = "abc123";
    long time = new Date().getTime();
    final List<TxInput> inputs = asList(new TxInput("tx1", 0, null), new TxInput("tx1", 1, null));
    final List<RawTxOutput> outputs = asList(new RawTxOutput(0, 101, "tx1", null, null, null, height));
    RawTx rawTx = new RawTx("vo", height, hash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(outputs));
    // Return one spendable txoutputs with value, for three test cases
    // 1) - null, 0     -> not BSQ transaction
    // 2) - 100, null   -> BSQ transaction
    // 3) - 0, 100      -> BSQ transaction
    new Expectations(bsqStateService) {

        {
            // Expectations can be recorded on mocked instances, either with specific matching arguments or catch all
            // http://jmockit.github.io/tutorial/Mocking.html#results
            // Results are returned in the order they're recorded, so in this case for the first call to
            // getSpendableTxOutput("tx1", 0) the return value will be Optional.empty()
            // for the second call the return is Optional.of(new TxOutput(0,... and so on
            bsqStateService.getUnspentTxOutput(new TxOutputKey("tx1", 0));
            result = Optional.empty();
            result = Optional.of(new RawTxOutput(0, 100, "txout1", null, null, null, height));
            result = Optional.of(new RawTxOutput(0, 0, "txout1", null, null, null, height));
            bsqStateService.getUnspentTxOutput(new TxOutputKey("tx1", 1));
            result = Optional.of(new RawTxOutput(0, 0, "txout2", null, null, null, height));
            result = Optional.empty();
            result = Optional.of(new RawTxOutput(0, 100, "txout2", null, null, null, height));
        }
    };
    String genesisTxId = "genesisTxId";
    int blockHeight = 200;
    String blockHash = "abc123";
    Coin genesisTotalSupply = Coin.parseCoin("2.5");
    // First time there is no BSQ value to spend so it's not a bsq transaction
    assertFalse(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
    // Second time there is BSQ in the first txout
    assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
    // Third time there is BSQ in the second txout
    assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
}
Also used : Expectations(mockit.Expectations) Coin(org.bitcoinj.core.Coin) RawTxOutput(bisq.core.dao.state.blockchain.RawTxOutput) RawTx(bisq.core.dao.state.blockchain.RawTx) TxOutputKey(bisq.core.dao.state.blockchain.TxOutputKey) Date(java.util.Date) TxInput(bisq.core.dao.state.blockchain.TxInput) Test(org.junit.Test)

Example 3 with TxOutputKey

use of bisq.core.dao.state.blockchain.TxOutputKey in project bisq-core by bisq-network.

the class BsqWalletService method updateBsqBalance.

// /////////////////////////////////////////////////////////////////////////////////////////
// Balance
// /////////////////////////////////////////////////////////////////////////////////////////
private void updateBsqBalance() {
    unverifiedBalance = Coin.valueOf(getTransactions(false).stream().filter(tx -> tx.getConfidence().getConfidenceType() == PENDING).mapToLong(tx -> {
        // Sum up outputs into BSQ wallet and subtract the inputs using lockup or unlocking
        // outputs since those inputs will be accounted for in lockupBondsBalance and
        // unlockingBondsBalance
        long outputs = tx.getOutputs().stream().filter(out -> out.isMine(wallet)).mapToLong(out -> out.getValue().value).sum();
        // Account for spending of locked connectedOutputs
        long lockedInputs = tx.getInputs().stream().filter(in -> {
            TransactionOutput connectedOutput = in.getConnectedOutput();
            if (connectedOutput != null) {
                Transaction parentTransaction = connectedOutput.getParentTransaction();
                // TODO SQ
                if (parentTransaction != null) /* &&
                                                    parentTransaction.getConfidence().getConfidenceType() == BUILDING*/
                {
                    TxOutputKey key = new TxOutputKey(parentTransaction.getHashAsString(), connectedOutput.getIndex());
                    return (connectedOutput.isMine(wallet) && (bsqStateService.isLockupOutput(key) || bsqStateService.isUnlockingOutput(key)));
                }
            }
            return false;
        }).mapToLong(in -> in != null ? in.getValue().value : 0).sum();
        return outputs - lockedInputs;
    }).sum());
    Set<String> confirmedTxIdSet = getTransactions(false).stream().filter(tx -> tx.getConfidence().getConfidenceType() == BUILDING).map(Transaction::getHashAsString).collect(Collectors.toSet());
    lockedForVotingBalance = Coin.valueOf(bsqStateService.getUnspentBlindVoteStakeTxOutputs().stream().filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())).mapToLong(TxOutput::getValue).sum());
    lockupBondsBalance = Coin.valueOf(bsqStateService.getLockupTxOutputs().stream().filter(txOutput -> bsqStateService.isUnspent(txOutput.getKey())).filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())).mapToLong(TxOutput::getValue).sum());
    unlockingBondsBalance = Coin.valueOf(bsqStateService.getUnspentUnlockingTxOutputsStream().filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())).mapToLong(TxOutput::getValue).sum());
    availableBalance = bsqCoinSelector.select(NetworkParameters.MAX_MONEY, wallet.calculateAllSpendCandidates()).valueGathered;
    if (availableBalance.isNegative())
        availableBalance = Coin.ZERO;
    availableNonBsqBalance = nonBsqCoinSelector.select(NetworkParameters.MAX_MONEY, wallet.calculateAllSpendCandidates()).valueGathered;
    bsqBalanceListeners.forEach(e -> e.onUpdateBalances(availableBalance, availableNonBsqBalance, unverifiedBalance, lockedForVotingBalance, lockupBondsBalance, unlockingBondsBalance));
}
Also used : Transaction(org.bitcoinj.core.Transaction) TransactionConfidence(org.bitcoinj.core.TransactionConfidence) Getter(lombok.Getter) Coin(org.bitcoinj.core.Coin) ScriptException(org.bitcoinj.core.ScriptException) CoinSelection(org.bitcoinj.wallet.CoinSelection) TxOutput(bisq.core.dao.state.blockchain.TxOutput) Wallet(org.bitcoinj.wallet.Wallet) FXCollections(javafx.collections.FXCollections) Block(bisq.core.dao.state.blockchain.Block) Function(java.util.function.Function) Inject(javax.inject.Inject) HashSet(java.util.HashSet) NetworkParameters(org.bitcoinj.core.NetworkParameters) Preconditions.checkArgument(com.google.common.base.Preconditions.checkArgument) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) SendRequest(org.bitcoinj.wallet.SendRequest) Map(java.util.Map) AbstractWalletEventListener(org.bitcoinj.wallet.listeners.AbstractWalletEventListener) BUILDING(org.bitcoinj.core.TransactionConfidence.ConfidenceType.BUILDING) PENDING(org.bitcoinj.core.TransactionConfidence.ConfidenceType.PENDING) AddressFormatException(org.bitcoinj.core.AddressFormatException) WalletException(bisq.core.btc.exceptions.WalletException) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) BlockChain(org.bitcoinj.core.BlockChain) Tx(bisq.core.dao.state.blockchain.Tx) Preconditions.checkNotNull(com.google.common.base.Preconditions.checkNotNull) BsqStateService(bisq.core.dao.state.BsqStateService) Set(java.util.Set) TxOutputKey(bisq.core.dao.state.blockchain.TxOutputKey) BisqEnvironment(bisq.core.app.BisqEnvironment) CopyOnWriteArraySet(java.util.concurrent.CopyOnWriteArraySet) InsufficientMoneyException(org.bitcoinj.core.InsufficientMoneyException) Collectors(java.util.stream.Collectors) ECKey(org.bitcoinj.core.ECKey) List(java.util.List) Slf4j(lombok.extern.slf4j.Slf4j) Script(org.bitcoinj.script.Script) TransactionInput(org.bitcoinj.core.TransactionInput) Preferences(bisq.core.user.Preferences) BsqStateListener(bisq.core.dao.state.BsqStateListener) TransactionOutput(org.bitcoinj.core.TransactionOutput) Optional(java.util.Optional) FeeService(bisq.core.provider.fee.FeeService) Address(org.bitcoinj.core.Address) ObservableList(javafx.collections.ObservableList) Restrictions(bisq.core.btc.Restrictions) TransactionOutput(org.bitcoinj.core.TransactionOutput) Transaction(org.bitcoinj.core.Transaction) TxOutputKey(bisq.core.dao.state.blockchain.TxOutputKey)

Example 4 with TxOutputKey

use of bisq.core.dao.state.blockchain.TxOutputKey in project bisq-core by bisq-network.

the class TxParser method findTx.

// Apply state changes to tx, inputs and outputs
// return true if any input contained BSQ
// Any tx with BSQ input is a BSQ tx (except genesis tx but that is not handled in
// that class).
// There might be txs without any valid BSQ txOutput but we still keep track of it,
// for instance to calculate the total burned BSQ.
public Optional<Tx> findTx(RawTx rawTx, String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply) {
    txInputParser = new TxInputParser(bsqStateService);
    txOutputParser = new TxOutputParser(bsqStateService);
    // Let's see if we have a genesis tx
    Optional<TempTx> optionalGenesisTx = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
    if (optionalGenesisTx.isPresent()) {
        TempTx genesisTx = optionalGenesisTx.get();
        txOutputParser.processGenesisTxOutput(genesisTx);
        return Optional.of(Tx.fromTempTx(genesisTx));
    }
    // If it is not a genesis tx we continue to parse to see if it is a valid BSQ tx.
    int blockHeight = rawTx.getBlockHeight();
    // We could pass tx also to the sub validators but as long we have not refactored the validators to pure
    // functions lets use the parsingModel.
    TempTx tempTx = TempTx.fromRawTx(rawTx);
    for (int inputIndex = 0; inputIndex < tempTx.getTxInputs().size(); inputIndex++) {
        TxInput input = tempTx.getTxInputs().get(inputIndex);
        TxOutputKey outputKey = input.getConnectedTxOutputKey();
        txInputParser.process(outputKey, blockHeight, rawTx.getId(), inputIndex);
    }
    long accumulatedInputValue = txInputParser.getAccumulatedInputValue();
    txOutputParser.setAvailableInputValue(accumulatedInputValue);
    txOutputParser.setUnlockBlockHeight(txInputParser.getUnlockBlockHeight());
    txOutputParser.setOptionalSpentLockupTxOutput(txInputParser.getOptionalSpentLockupTxOutput());
    // TODO remove
    txOutputParser.setTempTx(tempTx);
    boolean hasBsqInputs = accumulatedInputValue > 0;
    if (hasBsqInputs) {
        final List<TempTxOutput> outputs = tempTx.getTempTxOutputs();
        // We start with last output as that might be an OP_RETURN output and gives us the specific tx type, so it is
        // easier and cleaner at parsing the other outputs to detect which kind of tx we deal with.
        // Setting the opReturn type here does not mean it will be a valid BSQ tx as the checks are only partial and
        // BSQ inputs are not verified yet.
        // We keep the temporary opReturn type in the parsingModel object.
        checkArgument(!outputs.isEmpty(), "outputs must not be empty");
        int lastIndex = outputs.size() - 1;
        txOutputParser.processOpReturnCandidate(outputs.get(lastIndex));
        // We iterate all outputs including the opReturn to do a full validation including the BSQ fee
        for (int index = 0; index < outputs.size(); index++) {
            boolean isLastOutput = index == lastIndex;
            txOutputParser.processTxOutput(isLastOutput, outputs.get(index), index);
        }
        remainingInputValue = txOutputParser.getAvailableInputValue();
        processOpReturnType(blockHeight, tempTx);
        // We don't allow multiple opReturn outputs (they are non-standard but to be safe lets check it)
        long numOpReturnOutputs = tempTx.getTempTxOutputs().stream().filter(txOutputParser::isOpReturnOutput).count();
        if (numOpReturnOutputs <= 1) {
            boolean isAnyTxOutputTypeUndefined = tempTx.getTempTxOutputs().stream().anyMatch(txOutput -> TxOutputType.UNDEFINED == txOutput.getTxOutputType());
            if (!isAnyTxOutputTypeUndefined) {
                // TODO(chirhonul): we don't modify the tempTx within the call below, so maybe we should
                // use RawTx?
                TxType txType = TxParser.getBisqTxType(tempTx, txOutputParser.getOptionalOpReturnTypeCandidate().isPresent(), remainingInputValue, getOptionalOpReturnType());
                tempTx.setTxType(txType);
                if (remainingInputValue > 0)
                    tempTx.setBurntFee(remainingInputValue);
            } else {
                tempTx.setTxType(TxType.INVALID);
                String msg = "We have undefined txOutput types which must not happen. tx=" + tempTx;
                DevEnv.logErrorAndThrowIfDevMode(msg);
            }
        } else {
            // We don't consider a tx with multiple OpReturn outputs valid.
            tempTx.setTxType(TxType.INVALID);
            String msg = "Invalid tx. We have multiple opReturn outputs. tx=" + tempTx;
            log.warn(msg);
        }
    }
    if (hasBsqInputs || txInputParser.getBurntBondValue() > 0)
        return Optional.of(Tx.fromTempTx(tempTx));
    else
        return Optional.empty();
}
Also used : TempTx(bisq.core.dao.state.blockchain.TempTx) TxType(bisq.core.dao.state.blockchain.TxType) TempTxOutput(bisq.core.dao.state.blockchain.TempTxOutput) TxOutputKey(bisq.core.dao.state.blockchain.TxOutputKey) TxInput(bisq.core.dao.state.blockchain.TxInput)

Aggregations

TxOutputKey (bisq.core.dao.state.blockchain.TxOutputKey)4 TxInput (bisq.core.dao.state.blockchain.TxInput)2 Coin (org.bitcoinj.core.Coin)2 Transaction (org.bitcoinj.core.Transaction)2 BisqEnvironment (bisq.core.app.BisqEnvironment)1 Restrictions (bisq.core.btc.Restrictions)1 TransactionVerificationException (bisq.core.btc.exceptions.TransactionVerificationException)1 WalletException (bisq.core.btc.exceptions.WalletException)1 BsqStateListener (bisq.core.dao.state.BsqStateListener)1 BsqStateService (bisq.core.dao.state.BsqStateService)1 Block (bisq.core.dao.state.blockchain.Block)1 RawTx (bisq.core.dao.state.blockchain.RawTx)1 RawTxOutput (bisq.core.dao.state.blockchain.RawTxOutput)1 TempTx (bisq.core.dao.state.blockchain.TempTx)1 TempTxOutput (bisq.core.dao.state.blockchain.TempTxOutput)1 Tx (bisq.core.dao.state.blockchain.Tx)1 TxOutput (bisq.core.dao.state.blockchain.TxOutput)1 TxType (bisq.core.dao.state.blockchain.TxType)1 FeeService (bisq.core.provider.fee.FeeService)1 Preferences (bisq.core.user.Preferences)1