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);
}
}
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;
}
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);
}
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();
}
});
}
}
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();
}
Aggregations