Search in sources :

Example 1 with TxType

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

the class VoteResultConsensus method getBlindVoteTx.

public static Tx getBlindVoteTx(TxOutput blindVoteStakeOutput, BsqStateService bsqStateService, PeriodService periodService, int chainHeight) throws VoteResultException {
    try {
        String blindVoteTxId = blindVoteStakeOutput.getTxId();
        Optional<Tx> optionalBlindVoteTx = bsqStateService.getTx(blindVoteTxId);
        checkArgument(optionalBlindVoteTx.isPresent(), "blindVoteTx with txId " + blindVoteTxId + " not found.");
        Tx blindVoteTx = optionalBlindVoteTx.get();
        Optional<TxType> optionalTxType = bsqStateService.getOptionalTxType(blindVoteTx.getId());
        checkArgument(optionalTxType.isPresent(), "optionalTxType must be present");
        checkArgument(optionalTxType.get() == TxType.BLIND_VOTE, "blindVoteTx must have type BLIND_VOTE");
        checkArgument(periodService.isTxInCorrectCycle(blindVoteTx.getBlockHeight(), chainHeight), "blindVoteTx is not in correct cycle. blindVoteTx.getBlockHeight()=" + blindVoteTx.getBlockHeight());
        checkArgument(periodService.isInPhase(blindVoteTx.getBlockHeight(), DaoPhase.Phase.BLIND_VOTE), "blindVoteTx is not in BLIND_VOTE phase. blindVoteTx.getBlockHeight()=" + blindVoteTx.getBlockHeight());
        return blindVoteTx;
    } catch (Throwable t) {
        throw new VoteResultException(t);
    }
}
Also used : Tx(bisq.core.dao.state.blockchain.Tx) TxType(bisq.core.dao.state.blockchain.TxType)

Example 2 with TxType

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

the class TxParser method getBisqTxType.

/**
 * Retrieve the type of the transaction, assuming it is relevant to bisq.
 *
 * @param tx                    The temporary transaction.
 * @param hasOpReturnCandidate  True if we have a candidate for an OP_RETURN.
 * @param remainingInputValue   The remaining value of inputs not yet accounted for, in satoshi.
 * @param optionalOpReturnType  If present, the OP_RETURN type of the transaction.
 * @return The type of the transaction, if it is relevant to bisq.
 */
@VisibleForTesting
static TxType getBisqTxType(TempTx tx, boolean hasOpReturnCandidate, long remainingInputValue, Optional<OpReturnType> optionalOpReturnType) {
    TxType txType;
    // We need to have at least one BSQ output
    if (optionalOpReturnType.isPresent()) {
        log.debug("Optional OP_RETURN type is present for tx.");
        txType = TxParser.getTxTypeForOpReturn(tx, optionalOpReturnType.get());
    } else if (!hasOpReturnCandidate) {
        log.debug("No optional OP_RETURN type and no OP_RETURN candidate is present for tx.");
        boolean bsqFeesBurnt = remainingInputValue > 0;
        if (bsqFeesBurnt) {
            // Burned fee but no opReturn
            txType = TxType.PAY_TRADE_FEE;
        } else if (tx.getTempTxOutputs().get(0).getTxOutputType() == TxOutputType.UNLOCK) {
            txType = TxType.UNLOCK;
        } else {
            log.debug("No burned fee and no OP_RETURN, so this is a TRANSFER_BSQ tx.");
            txType = TxType.TRANSFER_BSQ;
        }
    } else {
        log.debug("No optional OP_RETURN type is present for tx but we do have an OP_RETURN candidate, so it failed validation.");
        txType = TxType.INVALID;
    }
    return txType;
}
Also used : TxType(bisq.core.dao.state.blockchain.TxType) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 3 with TxType

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

the class TxParserTest method testGetBisqTxType.

@Test
public void testGetBisqTxType() {
    // Thu Jun 20 14:04:25 CEST 2013
    long time = 1371729865;
    final List<TxInput> inputs = Arrays.asList(new TxInput("tx0", 0, null), new TxInput("tx1", 1, null));
    RawTxOutput output = new RawTxOutput(0, 123, null, null, null, null, 100);
    RawTx rawTx = new RawTx("faketx0", 100, "fakeblock0", time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)));
    TempTx tempTx = TempTx.fromRawTx(rawTx);
    boolean hasOpReturnCandidate = true;
    long remainingInputValue = 0;
    Optional<OpReturnType> optionalOpReturnType = Optional.empty();
    TxType result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    TxType want = TxType.INVALID;
    Assert.assertEquals("With an OP_RETURN candidate but no optional OP_RETURN type, this tx should be invalid.", want, result);
    hasOpReturnCandidate = false;
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.TRANSFER_BSQ;
    Assert.assertEquals("With no OP_RETURN candidate and no optional OP_RETURN type, this should be a BSQ transfer tx.", want, result);
    // todo(chirhonul): this is very likely incorrect, we should see the tx as INVALID if
    // !hasOpReturnCandidate but optionalOpReturnType.
    hasOpReturnCandidate = false;
    optionalOpReturnType = Optional.of(OpReturnType.LOCKUP);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.LOCKUP;
    Assert.assertEquals("With no OP_RETURN candidate and optional OP_RETURN type of LOCKUP, this should be a LOCKUP tx.", want, result);
    hasOpReturnCandidate = true;
    optionalOpReturnType = Optional.of(OpReturnType.BLIND_VOTE);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.BLIND_VOTE;
    Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of BLIND_VOTE, this should be a BLIND_VOTE tx.", want, result);
    hasOpReturnCandidate = true;
    optionalOpReturnType = Optional.of(OpReturnType.VOTE_REVEAL);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.VOTE_REVEAL;
    Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of VOTE_REVEAL, this should be a VOTE_REVEAL tx.", want, result);
    hasOpReturnCandidate = true;
    optionalOpReturnType = Optional.of(OpReturnType.PROPOSAL);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.PROPOSAL;
    Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of PROPOSAL, this should be a PROPOSAL tx.", want, result);
    hasOpReturnCandidate = true;
    optionalOpReturnType = Optional.of(OpReturnType.COMPENSATION_REQUEST);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.INVALID;
    Assert.assertEquals("COMPENSATION_REQUEST has fewer than three outputs, this should be a INVALID tx.", want, result);
    RawTxOutput output1 = new RawTxOutput(0, 123, null, null, null, null, 100);
    RawTxOutput output2 = new RawTxOutput(0, 456, null, null, null, null, 100);
    RawTxOutput output3 = new RawTxOutput(0, 678, null, null, null, null, 100);
    rawTx = new RawTx("faketx1", 200, "fakeblock1", time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output1, output2, output3)));
    tempTx = TempTx.fromRawTx(rawTx);
    hasOpReturnCandidate = true;
    optionalOpReturnType = Optional.of(OpReturnType.COMPENSATION_REQUEST);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.INVALID;
    Assert.assertEquals("Output 1 at COMPENSATION_REQUEST has to be a ISSUANCE_CANDIDATE_OUTPUT, this should be a INVALID tx.", want, result);
    hasOpReturnCandidate = true;
    optionalOpReturnType = Optional.of(OpReturnType.COMPENSATION_REQUEST);
    tempTx.getTempTxOutputs().get(1).setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT);
    result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
    want = TxType.COMPENSATION_REQUEST;
    Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of COMPENSATION_REQUEST, this should be a COMPENSATION_REQUEST tx.", want, result);
}
Also used : TempTx(bisq.core.dao.state.blockchain.TempTx) RawTxOutput(bisq.core.dao.state.blockchain.RawTxOutput) TxType(bisq.core.dao.state.blockchain.TxType) RawTx(bisq.core.dao.state.blockchain.RawTx) OpReturnType(bisq.core.dao.state.blockchain.OpReturnType) TxInput(bisq.core.dao.state.blockchain.TxInput) Test(org.junit.Test)

Example 4 with TxType

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

the class JsonBlockChainExporter method maybeExport.

public void maybeExport() {
    if (dumpBlockchainData) {
        ListenableFuture<Void> future = executor.submit(() -> {
            final BsqState bsqStateClone = bsqStateService.getClone();
            Map<String, Tx> txMap = bsqStateService.getBlocksFromState(bsqStateClone).stream().filter(Objects::nonNull).flatMap(block -> block.getTxs().stream()).collect(Collectors.toMap(Tx::getId, tx -> tx));
            for (Tx tx : txMap.values()) {
                String txId = tx.getId();
                final Optional<TxType> optionalTxType = bsqStateService.getOptionalTxType(txId);
                optionalTxType.ifPresent(txType1 -> {
                    JsonTxType txType = txType1 != TxType.UNDEFINED_TX_TYPE ? JsonTxType.valueOf(txType1.name()) : null;
                    List<JsonTxOutput> outputs = new ArrayList<>();
                    tx.getTxOutputs().forEach(txOutput -> {
                        final Optional<SpentInfo> optionalSpentInfo = bsqStateService.getSpentInfo(txOutput);
                        final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(txOutput);
                        final PubKeyScript pubKeyScript = txOutput.getPubKeyScript();
                        final JsonTxOutput outputForJson = new JsonTxOutput(txId, txOutput.getIndex(), isBsqOutput ? txOutput.getValue() : 0, !isBsqOutput ? txOutput.getValue() : 0, txOutput.getBlockHeight(), isBsqOutput, bsqStateService.getBurntFee(tx.getId()), txOutput.getAddress(), pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null, optionalSpentInfo.map(JsonSpentInfo::new).orElse(null), tx.getTime(), txType, txType != null ? txType.getDisplayString() : "", txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null);
                        outputs.add(outputForJson);
                        txOutputFileManager.writeToDisc(Utilities.objectToJson(outputForJson), outputForJson.getId());
                    });
                    List<JsonTxInput> inputs = tx.getTxInputs().stream().map(txInput -> {
                        Optional<TxOutput> optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput);
                        if (optionalTxOutput.isPresent()) {
                            final TxOutput connectedTxOutput = optionalTxOutput.get();
                            final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(connectedTxOutput);
                            return new JsonTxInput(txInput.getConnectedTxOutputIndex(), txInput.getConnectedTxOutputTxId(), connectedTxOutput.getValue(), isBsqOutput, connectedTxOutput.getAddress(), tx.getTime());
                        } else {
                            return null;
                        }
                    }).filter(Objects::nonNull).collect(Collectors.toList());
                    final JsonTx jsonTx = new JsonTx(txId, tx.getBlockHeight(), tx.getBlockHash(), tx.getTime(), inputs, outputs, txType, txType != null ? txType.getDisplayString() : "", bsqStateService.getBurntFee(tx.getId()));
                    txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), txId);
                });
            }
            jsonFileManager.writeToDisc(Utilities.objectToJson(bsqStateClone), "BsqStateService");
            return null;
        });
        Futures.addCallback(future, new FutureCallback<Void>() {

            public void onSuccess(Void ignore) {
                log.trace("onSuccess");
            }

            public void onFailure(@NotNull Throwable throwable) {
                log.error(throwable.toString());
                throwable.printStackTrace();
            }
        });
    }
}
Also used : ListenableFuture(com.google.common.util.concurrent.ListenableFuture) Utilities(bisq.common.util.Utilities) TxOutput(bisq.core.dao.state.blockchain.TxOutput) Inject(com.google.inject.Inject) DaoOptionKeys(bisq.core.dao.DaoOptionKeys) TxType(bisq.core.dao.state.blockchain.TxType) ArrayList(java.util.ArrayList) JsonFileManager(bisq.common.storage.JsonFileManager) Map(java.util.Map) Named(javax.inject.Named) SpentInfo(bisq.core.dao.state.blockchain.SpentInfo) BsqState(bisq.core.dao.state.BsqState) Utils(org.bitcoinj.core.Utils) Tx(bisq.core.dao.state.blockchain.Tx) BsqStateService(bisq.core.dao.state.BsqStateService) IOException(java.io.IOException) Collectors(java.util.stream.Collectors) FutureCallback(com.google.common.util.concurrent.FutureCallback) File(java.io.File) PubKeyScript(bisq.core.dao.state.blockchain.PubKeyScript) Objects(java.util.Objects) FileUtil(bisq.common.storage.FileUtil) Futures(com.google.common.util.concurrent.Futures) List(java.util.List) Slf4j(lombok.extern.slf4j.Slf4j) Paths(java.nio.file.Paths) Storage(bisq.common.storage.Storage) Optional(java.util.Optional) NotNull(org.jetbrains.annotations.NotNull) ListeningExecutorService(com.google.common.util.concurrent.ListeningExecutorService) TxOutput(bisq.core.dao.state.blockchain.TxOutput) BsqState(bisq.core.dao.state.BsqState) ArrayList(java.util.ArrayList) PubKeyScript(bisq.core.dao.state.blockchain.PubKeyScript) SpentInfo(bisq.core.dao.state.blockchain.SpentInfo) Tx(bisq.core.dao.state.blockchain.Tx) TxType(bisq.core.dao.state.blockchain.TxType) Objects(java.util.Objects)

Example 5 with TxType

use of bisq.core.dao.state.blockchain.TxType 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

TxType (bisq.core.dao.state.blockchain.TxType)5 TempTx (bisq.core.dao.state.blockchain.TempTx)2 Tx (bisq.core.dao.state.blockchain.Tx)2 TxInput (bisq.core.dao.state.blockchain.TxInput)2 FileUtil (bisq.common.storage.FileUtil)1 JsonFileManager (bisq.common.storage.JsonFileManager)1 Storage (bisq.common.storage.Storage)1 Utilities (bisq.common.util.Utilities)1 DaoOptionKeys (bisq.core.dao.DaoOptionKeys)1 BsqState (bisq.core.dao.state.BsqState)1 BsqStateService (bisq.core.dao.state.BsqStateService)1 OpReturnType (bisq.core.dao.state.blockchain.OpReturnType)1 PubKeyScript (bisq.core.dao.state.blockchain.PubKeyScript)1 RawTx (bisq.core.dao.state.blockchain.RawTx)1 RawTxOutput (bisq.core.dao.state.blockchain.RawTxOutput)1 SpentInfo (bisq.core.dao.state.blockchain.SpentInfo)1 TempTxOutput (bisq.core.dao.state.blockchain.TempTxOutput)1 TxOutput (bisq.core.dao.state.blockchain.TxOutput)1 TxOutputKey (bisq.core.dao.state.blockchain.TxOutputKey)1 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1