forked from bisq-network/bisq
/
BaseProposalFactory.java
118 lines (100 loc) · 5.16 KB
/
BaseProposalFactory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* This file is part of Bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.dao.governance.proposal;
import bisq.core.btc.exceptions.TransactionVerificationException;
import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.OpReturnType;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.common.app.Version;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
/**
* Base class for proposalFactory classes. Provides creation of a transaction. Proposal creation is delegated to
* concrete classes.
*/
@Slf4j
public abstract class BaseProposalFactory<R extends Proposal> {
protected final BsqWalletService bsqWalletService;
protected final BtcWalletService btcWalletService;
private final DaoStateService daoStateService;
private final ProposalValidator proposalValidator;
@Nullable
protected String name;
@Nullable
protected String link;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
protected BaseProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
ProposalValidator proposalValidator) {
this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService;
this.daoStateService = daoStateService;
this.proposalValidator = proposalValidator;
}
protected ProposalWithTransaction createProposalWithTransaction(String name,
String link)
throws ProposalValidationException, InsufficientMoneyException, TxException {
this.name = name;
this.link = link;
// As we don't know the txId yes we create a temp proposal with txId set to an empty string.
R proposal = createProposalWithoutTxId();
proposalValidator.validateDataFields(proposal);
Transaction transaction = createTransaction(proposal);
final Proposal proposalWithTxId = proposal.cloneProposalAndAddTxId(transaction.getHashAsString());
return new ProposalWithTransaction(proposalWithTxId, transaction);
}
protected abstract R createProposalWithoutTxId();
// We have txId set to null in proposal as we cannot know it before the tx is created.
// Once the tx is known we will create a new object including the txId.
// The hashOfPayload used in the opReturnData is created with the txId set to null.
private Transaction createTransaction(R proposal) throws InsufficientMoneyException, TxException {
try {
Coin fee = ProposalConsensus.getFee(daoStateService, daoStateService.getChainHeight());
// We create a prepared Bsq Tx for the proposal fee.
Transaction preparedBurnFeeTx = proposal instanceof IssuanceProposal ?
bsqWalletService.getPreparedIssuanceTx(fee) :
bsqWalletService.getPreparedProposalTx(fee);
// 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.
Transaction txWithBtcFee = completeTx(preparedBurnFeeTx, opReturnData, proposal);
// We sign the BSQ inputs of the final tx.
Transaction transaction = bsqWalletService.signTx(txWithBtcFee);
log.info("Proposal tx: " + transaction);
return transaction;
} catch (WalletException | TransactionVerificationException e) {
throw new TxException(e);
}
}
protected byte[] getOpReturnData(byte[] hashOfPayload) {
return ProposalConsensus.getOpReturnData(hashOfPayload, OpReturnType.PROPOSAL.getType(), Version.PROPOSAL);
}
protected Transaction completeTx(Transaction preparedBurnFeeTx, byte[] opReturnData, Proposal proposal)
throws WalletException, InsufficientMoneyException, TransactionVerificationException {
return btcWalletService.completePreparedBurnBsqTx(preparedBurnFeeTx, opReturnData);
}
}