Coverage Summary for Class: PeginInstructionsProvider (co.rsk.peg.pegininstructions)
Class |
Class, %
|
Method, %
|
Line, %
|
PeginInstructionsProvider |
0%
(0/1)
|
0%
(0/5)
|
0%
(0/51)
|
1 package co.rsk.peg.pegininstructions;
2
3 import co.rsk.bitcoinj.core.BtcTransaction;
4 import co.rsk.bitcoinj.core.TransactionOutput;
5 import co.rsk.bitcoinj.script.ScriptChunk;
6 import java.util.Arrays;
7 import java.util.List;
8 import java.util.Optional;
9 import org.bouncycastle.util.encoders.Hex;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12
13 public class PeginInstructionsProvider {
14
15 private static final Logger logger = LoggerFactory.getLogger(PeginInstructionsProvider.class);
16 private static final byte[] RSKT_HEX = Hex.decode("52534b54");
17
18 public Optional<PeginInstructions> buildPeginInstructions(BtcTransaction btcTx) throws
19 PeginInstructionsException {
20
21 logger.trace("[buildPeginInstructions] Using btc tx {}", btcTx.getHash());
22
23 PeginInstructionsBase peginInstructions;
24 byte[] opReturnOutputData;
25
26 try {
27 opReturnOutputData = extractOpReturnData(btcTx);
28 } catch (NoOpReturnException e) {
29 logger.trace("[buildPeginInstructions] {}", e.getMessage());
30 return Optional.empty();
31 } catch (Exception e) {
32 String message = String.format("Btc tx: %s has an invalid OP_RETURN structure", btcTx.getHash());
33 logger.debug(message);
34 throw new PeginInstructionsException(message, e);
35 }
36
37 logger.trace("[buildPeginInstructions] OP_RETURN data: {}", Hex.toHexString(opReturnOutputData));
38
39 int protocolVersion = PeginInstructionsBase.extractProtocolVersion(opReturnOutputData);
40
41 switch (protocolVersion) {
42 case 1:
43 logger.trace("[buildPeginInstructions] Going to build peginInstructions version 1..");
44 peginInstructions = new PeginInstructionsVersion1(btcTx.getParams());
45 break;
46 default:
47 logger.debug("[buildPeginInstructions] Invalid protocol version given: {}", protocolVersion);
48 throw new PeginInstructionsException("Invalid protocol version");
49 }
50
51 peginInstructions.parse(opReturnOutputData);
52 logger.trace("[buildPeginInstructions] Successfully created peginInstructions: {}",
53 peginInstructions.getClass());
54
55 return Optional.of(peginInstructions);
56 }
57
58 protected static byte[] extractOpReturnData(BtcTransaction btcTx) throws PeginInstructionsException {
59 logger.trace("[extractOpReturnData] Getting OP_RETURN data for btc tx: {}", btcTx.getHash());
60
61 byte[] data = new byte[]{};
62 int opReturnForRskOccurrences = 0;
63
64 for (int i = 0; i < btcTx.getOutputs().size(); i++) {
65 TransactionOutput txOutput = btcTx.getOutput(i);
66 if(hasOpReturnForRsk(txOutput)) {
67 data = txOutput.getScriptPubKey().getChunks().get(1).data;
68 opReturnForRskOccurrences++;
69 }
70 }
71
72 if (opReturnForRskOccurrences == 0) {
73 String message = String.format("No OP_RETURN output found for tx %s", btcTx.getHash());
74 throw new NoOpReturnException(message);
75 }
76
77 if (opReturnForRskOccurrences > 1) {
78 String message = String.format("Only one output with OP_RETURN for RSK is allowed. Found %d",
79 opReturnForRskOccurrences);
80 logger.debug("[extractOpReturnData] {}", message);
81 throw new PeginInstructionsException(message);
82 }
83
84 return data;
85 }
86
87 private static boolean hasOpReturnForRsk(TransactionOutput txOutput) {
88 if(txOutput.getScriptPubKey().isOpReturn()) {
89 // Check if it has data with `RSKT` prefix
90 List<ScriptChunk> chunksByOutput = txOutput.getScriptPubKey().getChunks();
91 if (chunksByOutput.size() > 1 &&
92 chunksByOutput.get(1).data != null &&
93 chunksByOutput.get(1).data.length >= 4) {
94 byte[] prefix = Arrays.copyOfRange(chunksByOutput.get(1).data, 0, 4);
95 if (Arrays.equals(prefix, RSKT_HEX)) {
96 return true;
97 }
98 }
99 }
100
101 return false;
102 }
103 }