Coverage Summary for Class: ReleaseTransactionBuilder (co.rsk.peg)
Class |
Method, %
|
Line, %
|
ReleaseTransactionBuilder |
0%
(0/13)
|
0%
(0/52)
|
ReleaseTransactionBuilder$BuildResult |
0%
(0/3)
|
0%
(0/5)
|
Total |
0%
(0/16)
|
0%
(0/57)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package co.rsk.peg;
20
21 import co.rsk.bitcoinj.core.*;
22 import co.rsk.bitcoinj.wallet.SendRequest;
23 import co.rsk.bitcoinj.wallet.Wallet;
24 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
25 import org.ethereum.config.blockchain.upgrades.ConsensusRule;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 import java.util.*;
30 import java.util.stream.Collectors;
31
32 /**
33 * Given a set of UTXOs, a ReleaseTransactionBuilder
34 * knows how to build a release transaction
35 * of a certain amount to a certain address,
36 * and how to signal the used UTXOs so they
37 * can be invalidated.
38 *
39 * @author Ariel Mendelzon
40 */
41 public class ReleaseTransactionBuilder {
42 public class BuildResult {
43 private final BtcTransaction btcTx;
44 private final List<UTXO> selectedUTXOs;
45
46 public BuildResult(BtcTransaction btcTx, List<UTXO> selectedUTXOs) {
47 this.btcTx = btcTx;
48 this.selectedUTXOs = selectedUTXOs;
49 }
50
51 public BtcTransaction getBtcTx() {
52 return btcTx;
53 }
54
55 public List<UTXO> getSelectedUTXOs() {
56 return selectedUTXOs;
57 }
58 }
59
60 private interface SendRequestConfigurator {
61 void configure(SendRequest sr);
62 }
63
64 private static final Logger logger = LoggerFactory.getLogger("ReleaseTransactionBuilder");
65
66 private final NetworkParameters params;
67 private final Wallet wallet;
68 private final Address changeAddress;
69 private final Coin feePerKb;
70 private final ActivationConfig.ForBlock activations;
71
72 public ReleaseTransactionBuilder(
73 NetworkParameters params,
74 Wallet wallet,
75 Address changeAddress,
76 Coin feePerKb,
77 ActivationConfig.ForBlock activations
78 ) {
79 this.params = params;
80 this.wallet = wallet;
81 this.changeAddress = changeAddress;
82 this.feePerKb = feePerKb;
83 this.activations = activations;
84 }
85
86 public Wallet getWallet() {
87 return wallet;
88 }
89
90 public Address getChangeAddress() {
91 return changeAddress;
92 }
93
94 public Coin getFeePerKb() {
95 return feePerKb;
96 }
97
98 public Optional<BuildResult> buildAmountTo(Address to, Coin amount) {
99 return buildWithConfiguration((SendRequest sr) -> {
100 sr.tx.addOutput(amount, to);
101 sr.changeAddress = changeAddress;
102 }, String.format("sending %s to %s", amount, to));
103 }
104
105 public Optional<BuildResult> buildEmptyWalletTo(Address to) {
106 return buildWithConfiguration((SendRequest sr) -> {
107 sr.tx.addOutput(Coin.ZERO, to);
108 sr.changeAddress = to;
109 sr.emptyWallet = true;
110 }, String.format("emptying wallet to %s", to));
111 }
112
113 private Optional<BuildResult> buildWithConfiguration(
114 SendRequestConfigurator sendRequestConfigurator,
115 String operationDescription) {
116
117 // Build a tx and send request and configure it
118 BtcTransaction btcTx = new BtcTransaction(params);
119
120 if (activations.isActive(ConsensusRule.RSKIP201)) {
121 btcTx.setVersion(2);
122 }
123
124 SendRequest sr = SendRequest.forTx(btcTx);
125 // Default settings
126 defaultSettingsConfigurator.configure(sr);
127 // Specific settings
128 sendRequestConfigurator.configure(sr);
129
130 try {
131 wallet.completeTx(sr);
132
133 // Disconnect input from output because we don't need the reference and it interferes serialization
134 for (TransactionInput transactionInput : btcTx.getInputs()) {
135 transactionInput.disconnect();
136 }
137
138 List<UTXO> selectedUTXOs = wallet
139 .getUTXOProvider().getOpenTransactionOutputs(wallet.getWatchedAddresses()).stream()
140 .filter(utxo ->
141 btcTx.getInputs().stream().anyMatch(input ->
142 input.getOutpoint().getHash().equals(utxo.getHash()) &&
143 input.getOutpoint().getIndex() == utxo.getIndex()
144 )
145 )
146 .collect(Collectors.toList());
147
148 return Optional.of(new BuildResult(btcTx, selectedUTXOs));
149 } catch (InsufficientMoneyException e) {
150 logger.warn(String.format("Not enough BTC in the wallet to complete %s", operationDescription), e);
151 // Comment out panic logging for now
152 // panicProcessor.panic("nomoney", "Not enough confirmed BTC in the federation wallet to complete " + rskTxHash + " " + btcTx);
153 return Optional.empty();
154 } catch (Wallet.CouldNotAdjustDownwards e) {
155 logger.warn(String.format("A user output could not be adjusted downwards to pay tx fees %s", operationDescription), e);
156 // Comment out panic logging for now
157 // panicProcessor.panic("couldnotadjustdownwards", "A user output could not be adjusted downwards to pay tx fees " + rskTxHash + " " + btcTx);
158 return Optional.empty();
159 } catch (Wallet.ExceededMaxTransactionSize e) {
160 logger.warn(String.format("Tx size too big %s", operationDescription), e);
161 // Comment out panic logging for now
162 // panicProcessor.panic("exceededmaxtransactionsize", "Tx size too big " + rskTxHash + " " + btcTx);
163 return Optional.empty();
164 } catch (UTXOProviderException e) {
165 logger.warn(String.format("UTXO provider exception sending %s", operationDescription), e);
166 // Comment out panic logging for now
167 // panicProcessor.panic("utxoprovider", "UTXO provider exception " + rskTxHash + " " + btcTx);
168 return Optional.empty();
169 }
170 }
171
172 private final SendRequestConfigurator defaultSettingsConfigurator = (SendRequest sr) -> {
173 sr.missingSigsMode = Wallet.MissingSigsMode.USE_OP_ZERO;
174 sr.feePerKb = getFeePerKb();
175 sr.shuffleOutputs = false;
176 sr.recipientsPayFees = true;
177 };
178 }