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 }