Merge pull request #2614 from ManfredKarrer/add-missing-check-for-mandatory-bsq-output

Add missing check for mandatory bsq output
This commit is contained in:
Christoph Atteneder 2019-04-02 09:35:10 +02:00 committed by GitHub
commit 6e2aaecfe7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 18 deletions

View File

@ -524,19 +524,23 @@ public class BsqWalletService extends WalletService implements DaoStateListener
// We create a tx with Bsq inputs for the fee and optional BSQ change output.
// As the fee amount will be missing in the output those BSQ fees are burned.
public Transaction getPreparedProposalTx(Coin fee) throws InsufficientBsqException {
return getPreparedBurnFeeTx(fee);
public Transaction getPreparedProposalTx(Coin fee, boolean requireChangeOutput) throws InsufficientBsqException {
return getPreparedBurnFeeTx(fee, requireChangeOutput);
}
public Transaction getPreparedBurnFeeTx(Coin fee) throws InsufficientBsqException {
return getPreparedBurnFeeTx(fee, false);
}
private Transaction getPreparedBurnFeeTx(Coin fee, boolean requireChangeOutput) throws InsufficientBsqException {
DaoKillSwitch.assertDaoIsNotDisabled();
final Transaction tx = new Transaction(params);
addInputsAndChangeOutputForTx(tx, fee, bsqCoinSelector);
addInputsAndChangeOutputForTx(tx, fee, bsqCoinSelector, requireChangeOutput);
// printTx("getPreparedFeeTx", tx);
return tx;
}
private void addInputsAndChangeOutputForTx(Transaction tx, Coin fee, BsqCoinSelector bsqCoinSelector)
private void addInputsAndChangeOutputForTx(Transaction tx, Coin fee, BsqCoinSelector bsqCoinSelector, boolean requireChangeOutput)
throws InsufficientBsqException {
Coin requiredInput;
// If our fee is less then dust limit we increase it so we are sure to not get any dust output.
@ -550,10 +554,19 @@ public class BsqWalletService extends WalletService implements DaoStateListener
try {
// TODO why is fee passed to getChange ???
Coin change = bsqCoinSelector.getChange(fee, coinSelection);
if (requireChangeOutput) {
checkArgument(change.isPositive(),
"This transaction requires a mandatory BSQ change output. " +
"You are missing " + Restrictions.getMinNonDustOutput().value / 100d +
" BSQ for a non dust change output.");
}
if (change.isPositive()) {
checkArgument(Restrictions.isAboveDust(change),
"The change output of " + change.value / 100d + " BSQ is below the min. dust value of "
+ Restrictions.getMinNonDustOutput().value / 100d + " BSQ.");
+ Restrictions.getMinNonDustOutput().value / 100d +
" BSQ. You are missing " + (Restrictions.getMinNonDustOutput().value - change.value) / 100d +
" BSQ for a non dust change output.");
tx.addOutput(change, getChangeAddress());
}
} catch (InsufficientMoneyException e) {
@ -572,7 +585,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener
DaoKillSwitch.assertDaoIsNotDisabled();
Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, stake, getUnusedAddress()));
addInputsAndChangeOutputForTx(tx, fee.add(stake), bsqCoinSelector);
addInputsAndChangeOutputForTx(tx, fee.add(stake), bsqCoinSelector, false);
//printTx("getPreparedBlindVoteTx", tx);
return tx;
}
@ -606,7 +619,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener
Transaction tx = new Transaction(params);
checkArgument(Restrictions.isAboveDust(lockupAmount), "The amount is too low (dust limit).");
tx.addOutput(new TransactionOutput(params, tx, lockupAmount, getUnusedAddress()));
addInputsAndChangeOutputForTx(tx, lockupAmount, bsqCoinSelector);
addInputsAndChangeOutputForTx(tx, lockupAmount, bsqCoinSelector, false);
printTx("prepareLockupTx", tx);
return tx;
}

View File

@ -85,16 +85,17 @@ public abstract class BaseProposalFactory<R extends Proposal> {
// The hashOfPayload used in the opReturnData is created with the txId set to null.
private Transaction createTransaction(R proposal) throws InsufficientMoneyException, TxException {
try {
final Coin fee = ProposalConsensus.getFee(daoStateService, daoStateService.getChainHeight());
Coin fee = ProposalConsensus.getFee(daoStateService, daoStateService.getChainHeight());
// We create a prepared Bsq Tx for the proposal fee.
final Transaction preparedBurnFeeTx = bsqWalletService.getPreparedProposalTx(fee);
boolean requireChangeOutput = proposal instanceof IssuanceProposal;
Transaction preparedBurnFeeTx = bsqWalletService.getPreparedProposalTx(fee, requireChangeOutput);
// payload does not have txId at that moment
byte[] hashOfPayload = ProposalConsensus.getHashOfPayload(proposal);
byte[] opReturnData = getOpReturnData(hashOfPayload);
// We add the BTC inputs for the miner fee.
final Transaction txWithBtcFee = completeTx(preparedBurnFeeTx, opReturnData, proposal);
Transaction txWithBtcFee = completeTx(preparedBurnFeeTx, opReturnData, proposal);
// We sign the BSQ inputs of the final tx.
Transaction transaction = bsqWalletService.signTx(txWithBtcFee);

View File

@ -194,15 +194,15 @@ public class TxOutputParser {
if (optionalOpReturnType.isPresent())
opReturnTypeCandidate = optionalOpReturnType.get();
TxOutputType bsqOutput;
TxOutputType txOutputType;
if (isFirstOutput && opReturnTypeCandidate == OpReturnType.BLIND_VOTE) {
bsqOutput = TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT;
txOutputType = TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT;
optionalBlindVoteLockStakeOutput = Optional.of(txOutput);
} else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.VOTE_REVEAL) {
bsqOutput = TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT;
txOutputType = TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT;
optionalVoteRevealUnlockStakeOutput = Optional.of(txOutput);
} else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.LOCKUP) {
bsqOutput = TxOutputType.LOCKUP_OUTPUT;
txOutputType = TxOutputType.LOCKUP_OUTPUT;
// We store the lockTime in the output which will be used as input for a unlock tx.
// That makes parsing of that data easier as if we would need to access it from the opReturn output of
@ -210,9 +210,9 @@ public class TxOutputParser {
txOutput.setLockTime(lockTime);
optionalLockupOutput = Optional.of(txOutput);
} else {
bsqOutput = TxOutputType.BSQ_OUTPUT;
txOutputType = TxOutputType.BSQ_OUTPUT;
}
txOutput.setTxOutputType(bsqOutput);
txOutput.setTxOutputType(txOutputType);
utxoCandidates.add(txOutput);
bsqOutputFound = true;

View File

@ -330,8 +330,10 @@ public class TxParser {
return true;
}
if (!bsqOutputFound) {
log.warn("Invalid Tx: No BSQ output found. tx=" + tempTx);
if ((tempTx.getTxType() == TxType.COMPENSATION_REQUEST ||
tempTx.getTxType() == TxType.REIMBURSEMENT_REQUEST)
&& !bsqOutputFound) {
log.warn("Invalid Tx: A compensation or reimbursement tx requires 1 BSQ output. Tx=" + tempTx);
return true;
}