Coverage Summary for Class: MinerUtils (co.rsk.mine)
Class |
Class, %
|
Method, %
|
Line, %
|
MinerUtils |
0%
(0/1)
|
0%
(0/12)
|
0%
(0/89)
|
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.mine;
20
21 import co.rsk.bitcoinj.core.BtcTransaction;
22 import co.rsk.bitcoinj.core.NetworkParameters;
23 import co.rsk.config.RskMiningConstants;
24 import co.rsk.core.Coin;
25 import co.rsk.core.RskAddress;
26 import co.rsk.core.bc.PendingState;
27 import co.rsk.crypto.Keccak256;
28 import co.rsk.db.RepositorySnapshot;
29 import co.rsk.remasc.RemascTransaction;
30 import org.bouncycastle.util.Arrays;
31 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
32 import org.ethereum.config.blockchain.upgrades.ConsensusRule;
33 import org.ethereum.core.Transaction;
34 import org.ethereum.core.TransactionPool;
35 import org.ethereum.rpc.TypeConverter;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import java.io.ByteArrayOutputStream;
40 import java.io.IOException;
41 import java.math.BigInteger;
42 import java.security.SecureRandom;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.function.Function;
48
49 public class MinerUtils {
50
51 private static final Logger logger = LoggerFactory.getLogger("minerserver");
52
53 public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransaction(co.rsk.bitcoinj.core.NetworkParameters params, MinerWork work) {
54 return getBitcoinMergedMiningCoinbaseTransaction(params, TypeConverter.stringHexToByteArray(work.getBlockHashForMergedMining()));
55 }
56
57 public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransaction(co.rsk.bitcoinj.core.NetworkParameters params, byte[] blockHashForMergedMining) {
58 SecureRandom random = new SecureRandom();
59 byte[] prefix = new byte[random.nextInt(1000)];
60 random.nextBytes(prefix);
61 byte[] bytes = Arrays.concatenate(prefix, RskMiningConstants.RSK_TAG, blockHashForMergedMining);
62
63 return getBitcoinCoinbaseTransaction(params, bytes);
64 }
65
66 public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinCoinbaseTransaction(co.rsk.bitcoinj.core.NetworkParameters params, byte[] additionalData) {
67
68 co.rsk.bitcoinj.core.BtcTransaction coinbaseTransaction = new co.rsk.bitcoinj.core.BtcTransaction(params);
69
70 // Add the data to the scriptSig of first input
71 co.rsk.bitcoinj.core.TransactionInput ti = new co.rsk.bitcoinj.core.TransactionInput(params, coinbaseTransaction, new byte[0]);
72 coinbaseTransaction.addInput(ti);
73
74 ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
75 co.rsk.bitcoinj.core.BtcECKey key = new co.rsk.bitcoinj.core.BtcECKey();
76 try {
77 co.rsk.bitcoinj.script.Script.writeBytes(scriptPubKeyBytes, key.getPubKey());
78 } catch (IOException e) {
79 throw new RuntimeException(e);
80 }
81 scriptPubKeyBytes.write(co.rsk.bitcoinj.script.ScriptOpCodes.OP_CHECKSIG);
82 coinbaseTransaction.addOutput(new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction, co.rsk.bitcoinj.core.Coin.valueOf(50, 0), scriptPubKeyBytes.toByteArray()));
83
84 coinbaseTransaction.addOutput(new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction, co.rsk.bitcoinj.core.Coin.valueOf(0), additionalData));
85
86 return coinbaseTransaction;
87 }
88
89 public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransactionWithTwoTags(
90 co.rsk.bitcoinj.core.NetworkParameters params,
91 MinerWork work,
92 MinerWork work2) {
93 return getBitcoinMergedMiningCoinbaseTransactionWithTwoTags(
94 params,
95 TypeConverter.stringHexToByteArray(work.getBlockHashForMergedMining()),
96 TypeConverter.stringHexToByteArray(work2.getBlockHashForMergedMining()));
97 }
98
99 public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransactionWithTwoTags(
100 co.rsk.bitcoinj.core.NetworkParameters params,
101 byte[] blockHashForMergedMining1,
102 byte[] blockHashForMergedMining2) {
103 co.rsk.bitcoinj.core.BtcTransaction coinbaseTransaction = new co.rsk.bitcoinj.core.BtcTransaction(params);
104 //Add a random number of random bytes before the RSK tag
105 SecureRandom random = new SecureRandom();
106 byte[] prefix = new byte[random.nextInt(1000)];
107 random.nextBytes(prefix);
108
109 byte[] bytes0 = Arrays.concatenate(RskMiningConstants.RSK_TAG, blockHashForMergedMining1);
110 // addsecond tag
111 byte[] bytes1 = Arrays.concatenate(bytes0, RskMiningConstants.RSK_TAG, blockHashForMergedMining2);
112
113 co.rsk.bitcoinj.core.TransactionInput ti = new co.rsk.bitcoinj.core.TransactionInput(params, coinbaseTransaction, prefix);
114 coinbaseTransaction.addInput(ti);
115 ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
116 co.rsk.bitcoinj.core.BtcECKey key = new co.rsk.bitcoinj.core.BtcECKey();
117 try {
118 co.rsk.bitcoinj.script.Script.writeBytes(scriptPubKeyBytes, key.getPubKey());
119 } catch (IOException e) {
120 throw new RuntimeException(e);
121 }
122 scriptPubKeyBytes.write(co.rsk.bitcoinj.script.ScriptOpCodes.OP_CHECKSIG);
123 coinbaseTransaction.addOutput(new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction, co.rsk.bitcoinj.core.Coin.valueOf(50, 0), scriptPubKeyBytes.toByteArray()));
124 // add opreturn output with two tags
125 ByteArrayOutputStream output2Bytes = new ByteArrayOutputStream();
126 output2Bytes.write(co.rsk.bitcoinj.script.ScriptOpCodes.OP_RETURN);
127
128 try {
129 co.rsk.bitcoinj.script.Script.writeBytes(output2Bytes, bytes1);
130 } catch (IOException e) {
131 throw new RuntimeException(e);
132 }
133 coinbaseTransaction.addOutput(
134 new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction, co.rsk.bitcoinj.core.Coin.valueOf(1), output2Bytes.toByteArray()));
135
136 return coinbaseTransaction;
137 }
138
139 public static co.rsk.bitcoinj.core.BtcBlock getBitcoinMergedMiningBlock(co.rsk.bitcoinj.core.NetworkParameters params, BtcTransaction transaction) {
140 return getBitcoinMergedMiningBlock(params, Collections.singletonList(transaction));
141 }
142
143 public static co.rsk.bitcoinj.core.BtcBlock getBitcoinMergedMiningBlock(co.rsk.bitcoinj.core.NetworkParameters params, List<BtcTransaction> transactions) {
144 co.rsk.bitcoinj.core.Sha256Hash prevBlockHash = co.rsk.bitcoinj.core.Sha256Hash.ZERO_HASH;
145 long time = System.currentTimeMillis() / 1000L;
146 long difficultyTarget = co.rsk.bitcoinj.core.Utils.encodeCompactBits(params.getMaxTarget());
147 return new co.rsk.bitcoinj.core.BtcBlock(params, params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.CURRENT), prevBlockHash, null, time, difficultyTarget, 0, transactions);
148 }
149
150 /**
151 * Takes in a proofBuilderFunction (e.g. buildFromTxHashes)
152 * and executes it on the builder corresponding to this block number.
153 */
154 public static byte[] buildMerkleProof(
155 ActivationConfig activationConfig,
156 Function<MerkleProofBuilder, byte[]> proofBuilderFunction,
157 long blockNumber) {
158 if (activationConfig.isActive(ConsensusRule.RSKIP92, blockNumber)) {
159 return proofBuilderFunction.apply(new Rskip92MerkleProofBuilder());
160 } else {
161 return proofBuilderFunction.apply(new GenesisMerkleProofBuilder());
162 }
163 }
164
165 public List<org.ethereum.core.Transaction> getAllTransactions(TransactionPool transactionPool) {
166
167 List<Transaction> txs = transactionPool.getPendingTransactions();
168
169 return PendingState.sortByPriceTakingIntoAccountSenderAndNonce(txs);
170 }
171
172 public List<org.ethereum.core.Transaction> filterTransactions(List<Transaction> txsToRemove, List<Transaction> txs, Map<RskAddress, BigInteger> accountNonces, RepositorySnapshot originalRepo, Coin minGasPrice) {
173 List<org.ethereum.core.Transaction> txsResult = new ArrayList<>();
174 for (org.ethereum.core.Transaction tx : txs) {
175 try {
176 Keccak256 hash = tx.getHash();
177 Coin txValue = tx.getValue();
178 BigInteger txNonce = new BigInteger(1, tx.getNonce());
179 RskAddress txSender = tx.getSender();
180 logger.debug("Examining tx={} sender: {} value: {} nonce: {}", hash, txSender, txValue, txNonce);
181
182 BigInteger expectedNonce;
183
184 if (accountNonces.containsKey(txSender)) {
185 expectedNonce = accountNonces.get(txSender).add(BigInteger.ONE);
186 } else {
187 expectedNonce = originalRepo.getNonce(txSender);
188 }
189
190 if (!(tx instanceof RemascTransaction) && tx.getGasPrice().compareTo(minGasPrice) < 0) {
191 logger.warn("Rejected tx={} because of low gas account {}, removing tx from pending state.", hash, txSender);
192
193 txsToRemove.add(tx);
194 continue;
195 }
196
197 if (!expectedNonce.equals(txNonce)) {
198 logger.warn("Invalid nonce, expected {}, found {}, tx={}", expectedNonce, txNonce, hash);
199 continue;
200 }
201
202 accountNonces.put(txSender, txNonce);
203
204 logger.debug("Accepted tx={} sender: {} value: {} nonce: {}", hash, txSender, txValue, txNonce);
205 } catch (Exception e) {
206 // Txs that can't be selected by any reason should be removed from pending state
207 logger.warn(String.format("Error when processing tx=%s", tx.getHash()), e);
208 if (txsToRemove != null) {
209 txsToRemove.add(tx);
210 } else {
211 logger.error("Can't remove invalid txs from pending state.");
212 }
213 continue;
214 }
215
216 txsResult.add(tx);
217 }
218
219 logger.debug("Ending getTransactions {}", txsResult.size());
220
221 return txsResult;
222 }
223 }