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