mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 18:03:12 +01:00
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:
commit
6e2aaecfe7
@ -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.
|
// 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.
|
// As the fee amount will be missing in the output those BSQ fees are burned.
|
||||||
public Transaction getPreparedProposalTx(Coin fee) throws InsufficientBsqException {
|
public Transaction getPreparedProposalTx(Coin fee, boolean requireChangeOutput) throws InsufficientBsqException {
|
||||||
return getPreparedBurnFeeTx(fee);
|
return getPreparedBurnFeeTx(fee, requireChangeOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transaction getPreparedBurnFeeTx(Coin fee) throws InsufficientBsqException {
|
public Transaction getPreparedBurnFeeTx(Coin fee) throws InsufficientBsqException {
|
||||||
|
return getPreparedBurnFeeTx(fee, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Transaction getPreparedBurnFeeTx(Coin fee, boolean requireChangeOutput) throws InsufficientBsqException {
|
||||||
DaoKillSwitch.assertDaoIsNotDisabled();
|
DaoKillSwitch.assertDaoIsNotDisabled();
|
||||||
final Transaction tx = new Transaction(params);
|
final Transaction tx = new Transaction(params);
|
||||||
addInputsAndChangeOutputForTx(tx, fee, bsqCoinSelector);
|
addInputsAndChangeOutputForTx(tx, fee, bsqCoinSelector, requireChangeOutput);
|
||||||
// printTx("getPreparedFeeTx", tx);
|
// printTx("getPreparedFeeTx", tx);
|
||||||
return 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 {
|
throws InsufficientBsqException {
|
||||||
Coin requiredInput;
|
Coin requiredInput;
|
||||||
// If our fee is less then dust limit we increase it so we are sure to not get any dust output.
|
// 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 {
|
try {
|
||||||
// TODO why is fee passed to getChange ???
|
// TODO why is fee passed to getChange ???
|
||||||
Coin change = bsqCoinSelector.getChange(fee, coinSelection);
|
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()) {
|
if (change.isPositive()) {
|
||||||
checkArgument(Restrictions.isAboveDust(change),
|
checkArgument(Restrictions.isAboveDust(change),
|
||||||
"The change output of " + change.value / 100d + " BSQ is below the min. dust value of "
|
"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());
|
tx.addOutput(change, getChangeAddress());
|
||||||
}
|
}
|
||||||
} catch (InsufficientMoneyException e) {
|
} catch (InsufficientMoneyException e) {
|
||||||
@ -572,7 +585,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener
|
|||||||
DaoKillSwitch.assertDaoIsNotDisabled();
|
DaoKillSwitch.assertDaoIsNotDisabled();
|
||||||
Transaction tx = new Transaction(params);
|
Transaction tx = new Transaction(params);
|
||||||
tx.addOutput(new TransactionOutput(params, tx, stake, getUnusedAddress()));
|
tx.addOutput(new TransactionOutput(params, tx, stake, getUnusedAddress()));
|
||||||
addInputsAndChangeOutputForTx(tx, fee.add(stake), bsqCoinSelector);
|
addInputsAndChangeOutputForTx(tx, fee.add(stake), bsqCoinSelector, false);
|
||||||
//printTx("getPreparedBlindVoteTx", tx);
|
//printTx("getPreparedBlindVoteTx", tx);
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
@ -606,7 +619,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener
|
|||||||
Transaction tx = new Transaction(params);
|
Transaction tx = new Transaction(params);
|
||||||
checkArgument(Restrictions.isAboveDust(lockupAmount), "The amount is too low (dust limit).");
|
checkArgument(Restrictions.isAboveDust(lockupAmount), "The amount is too low (dust limit).");
|
||||||
tx.addOutput(new TransactionOutput(params, tx, lockupAmount, getUnusedAddress()));
|
tx.addOutput(new TransactionOutput(params, tx, lockupAmount, getUnusedAddress()));
|
||||||
addInputsAndChangeOutputForTx(tx, lockupAmount, bsqCoinSelector);
|
addInputsAndChangeOutputForTx(tx, lockupAmount, bsqCoinSelector, false);
|
||||||
printTx("prepareLockupTx", tx);
|
printTx("prepareLockupTx", tx);
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
// The hashOfPayload used in the opReturnData is created with the txId set to null.
|
||||||
private Transaction createTransaction(R proposal) throws InsufficientMoneyException, TxException {
|
private Transaction createTransaction(R proposal) throws InsufficientMoneyException, TxException {
|
||||||
try {
|
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.
|
// 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
|
// payload does not have txId at that moment
|
||||||
byte[] hashOfPayload = ProposalConsensus.getHashOfPayload(proposal);
|
byte[] hashOfPayload = ProposalConsensus.getHashOfPayload(proposal);
|
||||||
byte[] opReturnData = getOpReturnData(hashOfPayload);
|
byte[] opReturnData = getOpReturnData(hashOfPayload);
|
||||||
|
|
||||||
// We add the BTC inputs for the miner fee.
|
// 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.
|
// We sign the BSQ inputs of the final tx.
|
||||||
Transaction transaction = bsqWalletService.signTx(txWithBtcFee);
|
Transaction transaction = bsqWalletService.signTx(txWithBtcFee);
|
||||||
|
@ -194,15 +194,15 @@ public class TxOutputParser {
|
|||||||
if (optionalOpReturnType.isPresent())
|
if (optionalOpReturnType.isPresent())
|
||||||
opReturnTypeCandidate = optionalOpReturnType.get();
|
opReturnTypeCandidate = optionalOpReturnType.get();
|
||||||
|
|
||||||
TxOutputType bsqOutput;
|
TxOutputType txOutputType;
|
||||||
if (isFirstOutput && opReturnTypeCandidate == OpReturnType.BLIND_VOTE) {
|
if (isFirstOutput && opReturnTypeCandidate == OpReturnType.BLIND_VOTE) {
|
||||||
bsqOutput = TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT;
|
txOutputType = TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT;
|
||||||
optionalBlindVoteLockStakeOutput = Optional.of(txOutput);
|
optionalBlindVoteLockStakeOutput = Optional.of(txOutput);
|
||||||
} else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.VOTE_REVEAL) {
|
} 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);
|
optionalVoteRevealUnlockStakeOutput = Optional.of(txOutput);
|
||||||
} else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.LOCKUP) {
|
} 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.
|
// 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
|
// 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);
|
txOutput.setLockTime(lockTime);
|
||||||
optionalLockupOutput = Optional.of(txOutput);
|
optionalLockupOutput = Optional.of(txOutput);
|
||||||
} else {
|
} else {
|
||||||
bsqOutput = TxOutputType.BSQ_OUTPUT;
|
txOutputType = TxOutputType.BSQ_OUTPUT;
|
||||||
}
|
}
|
||||||
txOutput.setTxOutputType(bsqOutput);
|
txOutput.setTxOutputType(txOutputType);
|
||||||
utxoCandidates.add(txOutput);
|
utxoCandidates.add(txOutput);
|
||||||
|
|
||||||
bsqOutputFound = true;
|
bsqOutputFound = true;
|
||||||
|
@ -330,8 +330,10 @@ public class TxParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bsqOutputFound) {
|
if ((tempTx.getTxType() == TxType.COMPENSATION_REQUEST ||
|
||||||
log.warn("Invalid Tx: No BSQ output found. tx=" + tempTx);
|
tempTx.getTxType() == TxType.REIMBURSEMENT_REQUEST)
|
||||||
|
&& !bsqOutputFound) {
|
||||||
|
log.warn("Invalid Tx: A compensation or reimbursement tx requires 1 BSQ output. Tx=" + tempTx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user