From cc7bea61b3b71ef70e8ed0fa592ce806e1b2c3ef Mon Sep 17 00:00:00 2001 From: julia-zack Date: Tue, 17 Sep 2024 15:58:37 -0300 Subject: [PATCH] Create new methods to handle btc tx deserialization without inputs to avoid passing boolean --- .../co/rsk/peg/BridgeSerializationUtils.java | 93 ++++++++++--------- .../src/main/java/co/rsk/peg/BridgeState.java | 2 +- .../co/rsk/peg/BridgeStorageProvider.java | 4 +- .../java/co/rsk/peg/StateForFederator.java | 2 +- .../rsk/peg/BridgeSerializationUtilsTest.java | 71 ++++++++++++-- 5 files changed, 117 insertions(+), 55 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java b/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java index f4240890753..0e87301d010 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java @@ -61,34 +61,51 @@ private BridgeSerializationUtils() { throw new IllegalAccessError("Utility class, do not instantiate it"); } + public static byte[] serializeRskTxHash(Keccak256 rskTxHash) { + return RLP.encodeElement(rskTxHash.getBytes()); + } + + private static Keccak256 deserializeRskTxHashFromRLPData(byte[] rlpData) { + return new Keccak256(rlpData); + } + public static byte[] serializeBtcTransaction(BtcTransaction btcTransaction) { return RLP.encodeElement(btcTransaction.bitcoinSerialize()); } - public static BtcTransaction deserializeBtcTransaction(byte[] data, NetworkParameters networkParameters) { + public static BtcTransaction deserializeBtcTransaction(byte[] data, NetworkParameters networkParameters, boolean txHasInputs) { if (data == null || data.length == 0) { return null; } - RLPElement rlpElement = RLP.decode2(data).get(0); - byte[] rawTx = rlpElement.getRLPData(); + byte[] rlpData = RLP.decode2(data).get(0).getRLPData(); + return deserializeBtcTransactionFromRLPData(rlpData, networkParameters, txHasInputs); + } + + private static BtcTransaction deserializeBtcTransactionFromRLPData(byte[] rlpData, NetworkParameters networkParameters, boolean txHasInputs) { + if (!txHasInputs) { + BtcTransaction tx = new BtcTransaction(networkParameters); + tx.parseNoInputs(rlpData); + return tx; + } + + return new BtcTransaction(networkParameters, rlpData); + } - return new BtcTransaction(networkParameters, rawTx); + public static BtcTransaction deserializeSvpFundTx(byte[] data, NetworkParameters networkParameters) { + return deserializeBtcTransaction(data, networkParameters, true); } - public static byte[] serializeRskTxWaitingForSignatures( - Map.Entry rskTxWaitingForSignaturesEntry) { - byte[][] serializedRskTxWaitingForSignaturesEntry = - serializeRskTxWaitingForSignaturesEntry(rskTxWaitingForSignaturesEntry); + public static byte[] serializeRskTxWaitingForSignatures(Map.Entry rskTxWaitingForSignaturesEntry) { + byte[][] serializedRskTxWaitingForSignaturesEntry = serializeRskTxWaitingForSignaturesEntry(rskTxWaitingForSignaturesEntry); return RLP.encodeList(serializedRskTxWaitingForSignaturesEntry); } - public static byte[] serializeRskTxsWaitingForSignatures( - SortedMap rskTxWaitingForSignaturesMap) { + public static byte[] serializeRskTxsWaitingForSignatures(SortedMap rskTxWaitingForSignaturesMap) { int numberOfRskTxsWaitingForSignatures = rskTxWaitingForSignaturesMap.size(); byte[][] serializedRskTxWaitingForSignaturesMap = new byte[numberOfRskTxsWaitingForSignatures * 2][]; - int n = 0; + int n = 0; for (Map.Entry rskTxWaitingForSignaturesEntry : rskTxWaitingForSignaturesMap.entrySet()) { byte[][] serializedRskTxWaitingForSignaturesEntry = serializeRskTxWaitingForSignaturesEntry(rskTxWaitingForSignaturesEntry); serializedRskTxWaitingForSignaturesMap[n++] = serializedRskTxWaitingForSignaturesEntry[0]; @@ -98,28 +115,23 @@ public static byte[] serializeRskTxsWaitingForSignatures( return RLP.encodeList(serializedRskTxWaitingForSignaturesMap); } - private static byte[][] serializeRskTxWaitingForSignaturesEntry( - Map.Entry rskTxWaitingForSignaturesEntry) { - byte[] serializedRskTxWaitingForSignaturesEntryKey = - RLP.encodeElement(rskTxWaitingForSignaturesEntry.getKey().getBytes()); - byte[] serializedRskTxWaitingForSignaturesEntryValue = - RLP.encodeElement(rskTxWaitingForSignaturesEntry.getValue().bitcoinSerialize()); + private static byte[][] serializeRskTxWaitingForSignaturesEntry(Map.Entry rskTxWaitingForSignaturesEntry) { + byte[] serializedRskTxWaitingForSignaturesEntryKey = serializeRskTxHash(rskTxWaitingForSignaturesEntry.getKey()); + byte[] serializedRskTxWaitingForSignaturesEntryValue = serializeBtcTransaction(rskTxWaitingForSignaturesEntry.getValue()); + return new byte[][] { serializedRskTxWaitingForSignaturesEntryKey, serializedRskTxWaitingForSignaturesEntryValue }; } - public static Map.Entry deserializeRskTxWaitingForSignatures( - byte[] data, NetworkParameters networkParameters, boolean noInputsTxs) { + public static Map.Entry deserializeRskTxWaitingForSignatures(byte[] data, NetworkParameters networkParameters) { if (data == null || data.length == 0) { return new AbstractMap.SimpleEntry<>(null, null); } RLPList rlpList = (RLPList) RLP.decode2(data).get(0); - - return deserializeRskTxWaitingForSignaturesEntry(rlpList, 0, networkParameters, noInputsTxs); + return deserializeRskTxWaitingForSignaturesEntry(rlpList, 0, networkParameters); } - public static SortedMap deserializeRskTxsWaitingForSignatures( - byte[] data, NetworkParameters networkParameters, boolean noInputsTxs) { + public static SortedMap deserializeRskTxsWaitingForSignatures(byte[] data, NetworkParameters networkParameters) { SortedMap rskTxsWaitingForSignaturesMap = new TreeMap<>(); if (data == null || data.length == 0) { @@ -131,28 +143,22 @@ public static SortedMap deserializeRskTxsWaitingForSi for (int k = 0; k < numberOfRskTxsWaitingForSignatures; k++) { Map.Entry rskTxWaitingForSignaturesEntry = - deserializeRskTxWaitingForSignaturesEntry(rlpList, k, networkParameters, noInputsTxs); + deserializeRskTxWaitingForSignaturesEntry(rlpList, k, networkParameters); rskTxsWaitingForSignaturesMap.put( rskTxWaitingForSignaturesEntry.getKey(), rskTxWaitingForSignaturesEntry.getValue()); } return rskTxsWaitingForSignaturesMap; } - - private static Map.Entry deserializeRskTxWaitingForSignaturesEntry( - RLPList rlpList, int index, NetworkParameters networkParameters, boolean noInputsTxs) { - Keccak256 rskTxHash = new Keccak256(rlpList.get(index * 2).getRLPData()); - byte[] payload = rlpList.get(index * 2 + 1).getRLPData(); - BtcTransaction tx; - if (noInputsTxs) { - tx = new BtcTransaction(networkParameters); - tx.parseNoInputs(payload); - } else { - tx = new BtcTransaction(networkParameters, payload); - } + private static Map.Entry deserializeRskTxWaitingForSignaturesEntry(RLPList rlpList, int index, NetworkParameters networkParameters) { + byte[] rskTxHashData = rlpList.get(index * 2).getRLPData(); + Keccak256 rskTxHash = deserializeRskTxHashFromRLPData(rskTxHashData); - return new AbstractMap.SimpleEntry<>(rskTxHash, tx); + byte[] btcTxData = rlpList.get(index * 2 + 1).getRLPData(); + BtcTransaction btcTx = deserializeBtcTransactionFromRLPData(btcTxData, networkParameters, true); + + return new AbstractMap.SimpleEntry<>(rskTxHash, btcTx); } public static byte[] serializeUTXOList(List list) { @@ -626,7 +632,8 @@ private static List deserializeReleaseRequestQueueWit byte[] addressBytes = rlpList.get(k * 3).getRLPData(); Address address = new Address(networkParameters, addressBytes); long amount = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 3 + 1).getRLPData()).longValue(); - Keccak256 txHash = new Keccak256(rlpList.get(k * 3 + 2).getRLPData()); + + Keccak256 txHash = deserializeRskTxHashFromRLPData(rlpList.get(k * 3 + 2).getRLPData()); entries.add(new ReleaseRequestQueue.Entry(address, Coin.valueOf(amount), txHash)); } @@ -650,7 +657,7 @@ public static byte[] serializePegoutsWaitingForConfirmations(PegoutsWaitingForCo int n = 0; for (PegoutsWaitingForConfirmations.Entry entry : entries) { - bytes[n++] = RLP.encodeElement(entry.getBtcTransaction().bitcoinSerialize()); + bytes[n++] = serializeBtcTransaction(entry.getBtcTransaction()); bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(entry.getPegoutCreationRskBlockNumber())); } @@ -665,9 +672,9 @@ public static byte[] serializePegoutsWaitingForConfirmationsWithTxHash(PegoutsWa int n = 0; for (PegoutsWaitingForConfirmations.Entry entry : entries) { - bytes[n++] = RLP.encodeElement(entry.getBtcTransaction().bitcoinSerialize()); + bytes[n++] = serializeBtcTransaction(entry.getBtcTransaction()); bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(entry.getPegoutCreationRskBlockNumber())); - bytes[n++] = RLP.encodeElement(entry.getPegoutCreationRskTxHash().getBytes()); + bytes[n++] = serializeRskTxHash(entry.getPegoutCreationRskTxHash()); } return RLP.encodeList(bytes); @@ -719,7 +726,7 @@ private static PegoutsWaitingForConfirmations deserializePegoutWaitingForConfirm BtcTransaction tx = new BtcTransaction(networkParameters, txPayload); long height = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 3 + 1).getRLPData()).longValue(); - Keccak256 rskTxHash = new Keccak256(rlpList.get(k * 3 + 2).getRLPData()); + Keccak256 rskTxHash = deserializeRskTxHashFromRLPData(rlpList.get(k * 3 + 2).getRLPData()); entries.add(new PegoutsWaitingForConfirmations.Entry(tx, height, rskTxHash)); } @@ -808,7 +815,7 @@ public static FlyoverFederationInformation deserializeFlyoverFederationInformati if (rlpList.size() != 2) { throw new RuntimeException(String.format("Invalid serialized Fast Bridge Federation: expected 2 value but got %d", rlpList.size())); } - Keccak256 derivationHash = new Keccak256(rlpList.get(0).getRLPData()); + Keccak256 derivationHash = deserializeRskTxHashFromRLPData(rlpList.get(0).getRLPData()); byte[] federationP2SH = rlpList.get(1).getRLPData(); return new FlyoverFederationInformation(derivationHash, federationP2SH, flyoverScriptHash); diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeState.java b/rskj-core/src/main/java/co/rsk/peg/BridgeState.java index e22eacb94ae..e35d396f939 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeState.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeState.java @@ -133,7 +133,7 @@ public static BridgeState create(BridgeConstants bridgeConstants, byte[] data, @ byte[] btcUTXOsBytes = rlpList.get(1).getRLPData(); List btcUTXOs = BridgeSerializationUtils.deserializeUTXOList(btcUTXOsBytes); byte[] rskTxsWaitingForSignaturesBytes = rlpList.get(2).getRLPData(); - SortedMap rskTxsWaitingForSignatures = BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(rskTxsWaitingForSignaturesBytes, bridgeConstants.getBtcParams(), false); + SortedMap rskTxsWaitingForSignatures = BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(rskTxsWaitingForSignaturesBytes, bridgeConstants.getBtcParams()); byte[] releaseRequestQueueBytes = rlpList.get(3).getRLPData(); ReleaseRequestQueue releaseRequestQueue = new ReleaseRequestQueue(BridgeSerializationUtils.deserializeReleaseRequestQueue(releaseRequestQueueBytes, bridgeConstants.getBtcParams(), shouldUsePapyrusEncoding(activations))); byte[] pegoutsWaitingForConfirmationsBytes = rlpList.get(4).getRLPData(); diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java b/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java index 82bab907a18..dfb466bfec0 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java @@ -243,7 +243,7 @@ public SortedMap getPegoutsWaitingForSignatures() thr pegoutsWaitingForSignatures = getFromRepository( PEGOUTS_WAITING_FOR_SIGNATURES, - data -> BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(data, networkParameters, false) + data -> BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(data, networkParameters) ); return pegoutsWaitingForSignatures; } @@ -562,7 +562,7 @@ public Optional getSvpFundTxSigned() { } svpFundTxSigned = safeGetFromRepository(SVP_FUND_TX_SIGNED, - data -> BridgeSerializationUtils.deserializeBtcTransaction(data, networkParameters)); + data -> BridgeSerializationUtils.deserializeSvpFundTx(data, networkParameters)); return Optional.ofNullable(svpFundTxSigned); } diff --git a/rskj-core/src/main/java/co/rsk/peg/StateForFederator.java b/rskj-core/src/main/java/co/rsk/peg/StateForFederator.java index eb2afa432f1..e4f5a328f7c 100644 --- a/rskj-core/src/main/java/co/rsk/peg/StateForFederator.java +++ b/rskj-core/src/main/java/co/rsk/peg/StateForFederator.java @@ -40,7 +40,7 @@ public StateForFederator(byte[] rlpData, NetworkParameters parameters) { RLPList rlpList = (RLPList) RLP.decode2(rlpData).get(0); byte[] encodedWaitingForSign = rlpList.get(0).getRLPData(); - this.rskTxsWaitingForSignatures = BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(encodedWaitingForSign, parameters, false); + this.rskTxsWaitingForSignatures = BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(encodedWaitingForSign, parameters); } public SortedMap getRskTxsWaitingForSignatures() { diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSerializationUtilsTest.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSerializationUtilsTest.java index 1353b4efe18..f93f7f9cf02 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSerializationUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSerializationUtilsTest.java @@ -88,7 +88,7 @@ class BridgeSerializationUtilsTest { private static final Address OTHER_ADDRESS = BitcoinTestUtils.createP2PKHAddress(NETWORK_PARAMETERS, "second"); @Test - void serializeAndDeserializeBtcTransaction_whenValidData_shouldReturnEqualResults() { + void serializeAndDeserializeBtcTransaction_withValidDataAndInputs_shouldReturnEqualResults() { // Arrange BtcTransaction fundTx = new BtcTransaction(NETWORK_PARAMETERS); fundTx.addOutput(Coin.FIFTY_COINS, ADDRESS); @@ -101,7 +101,7 @@ void serializeAndDeserializeBtcTransaction_whenValidData_shouldReturnEqualResult // Act byte[] serializedBtcTransaction = BridgeSerializationUtils.serializeBtcTransaction(btcTx); - BtcTransaction deserializedBtcTransaction = BridgeSerializationUtils.deserializeBtcTransaction(serializedBtcTransaction, NETWORK_PARAMETERS); + BtcTransaction deserializedBtcTransaction = BridgeSerializationUtils.deserializeBtcTransaction(serializedBtcTransaction, NETWORK_PARAMETERS, true); // Assert assertNotNull(serializedBtcTransaction); @@ -109,12 +109,67 @@ void serializeAndDeserializeBtcTransaction_whenValidData_shouldReturnEqualResult assertEquals(btcTx, deserializedBtcTransaction); } + @Test + void serializeAndDeserializeBtcTransaction_withValidDataAndWithoutInputs_shouldReturnEqualResults() { + // Arrange + BtcTransaction fundTx = new BtcTransaction(NETWORK_PARAMETERS); + fundTx.addOutput(Coin.FIFTY_COINS, ADDRESS); + fundTx.addOutput(Coin.FIFTY_COINS, OTHER_ADDRESS); + + BtcTransaction btcTx = new BtcTransaction(NETWORK_PARAMETERS); + btcTx.addOutput(Coin.COIN, OTHER_ADDRESS); + + // Act + byte[] serializedBtcTransaction = BridgeSerializationUtils.serializeBtcTransaction(btcTx); + BtcTransaction deserializedBtcTransaction = BridgeSerializationUtils.deserializeBtcTransaction(serializedBtcTransaction, NETWORK_PARAMETERS, false); + + // Assert + assertNotNull(serializedBtcTransaction); + assertNotNull(deserializedBtcTransaction); + assertEquals(btcTx, deserializedBtcTransaction); + } + + @ParameterizedTest + @NullSource + @EmptySource + void deserializeBtcTransaction_withInvalidData_shouldReturnNull(byte[] data) { + // Act + BtcTransaction deserializedTxWithInputs = BridgeSerializationUtils.deserializeBtcTransaction(data, NETWORK_PARAMETERS, true); + BtcTransaction deserializedTxWithoutInputs = BridgeSerializationUtils.deserializeBtcTransaction(data, NETWORK_PARAMETERS, false); + + // Assert + assertNull(deserializedTxWithInputs); + assertNull(deserializedTxWithoutInputs); + } + + @Test + void serializeAndDeserializeSvpFundTransaction_withValidData_shouldReturnEqualResults() { + // Arrange + BtcTransaction prevTx = new BtcTransaction(NETWORK_PARAMETERS); + prevTx.addOutput(Coin.FIFTY_COINS, ADDRESS); + prevTx.addOutput(Coin.FIFTY_COINS, OTHER_ADDRESS); + + BtcTransaction svpFundTx = new BtcTransaction(NETWORK_PARAMETERS); + svpFundTx.addInput(prevTx.getOutput(0)); + svpFundTx.addInput(prevTx.getOutput(1)); + svpFundTx.addOutput(Coin.COIN, OTHER_ADDRESS); + + // Act + byte[] serializedSvpFundTransaction = BridgeSerializationUtils.serializeBtcTransaction(svpFundTx); + BtcTransaction deserializedSvpFundTransaction = BridgeSerializationUtils.deserializeSvpFundTx(serializedSvpFundTransaction, NETWORK_PARAMETERS); + + // Assert + assertNotNull(serializedSvpFundTransaction); + assertNotNull(deserializedSvpFundTransaction); + assertEquals(svpFundTx, deserializedSvpFundTransaction); + } + @ParameterizedTest @NullSource @EmptySource - void deserializeBtcTransaction_whenInvalidData_shouldReturnNull(byte[] data) { + void deserializeSvpFundTransaction_withInvalidData_shouldReturnNull(byte[] data) { // Act - BtcTransaction result = BridgeSerializationUtils.deserializeBtcTransaction(data, NETWORK_PARAMETERS); + BtcTransaction result = BridgeSerializationUtils.deserializeSvpFundTx(data, NETWORK_PARAMETERS); // Assert assertNull(result); @@ -137,7 +192,7 @@ void serializeAndDeserializeRskTxWaitingForSignatures_whenValidData_shouldReturn byte[] serializedEntry = BridgeSerializationUtils.serializeRskTxWaitingForSignatures(pegoutTxWaitingForSiganturesEntry); Map.Entry deserializedEntry = - BridgeSerializationUtils.deserializeRskTxWaitingForSignatures(serializedEntry, NETWORK_PARAMETERS, false); + BridgeSerializationUtils.deserializeRskTxWaitingForSignatures(serializedEntry, NETWORK_PARAMETERS); // Assert assertNotNull(serializedEntry); @@ -154,7 +209,7 @@ void serializeAndDeserializeRskTxWaitingForSignatures_whenValidData_shouldReturn void deserializeRskTxWaitingForSignatures_whenInvalidData_shouldReturnEmptyResult(byte[] data) { // Act Map.Entry result = - BridgeSerializationUtils.deserializeRskTxWaitingForSignatures(data, NETWORK_PARAMETERS, false); + BridgeSerializationUtils.deserializeRskTxWaitingForSignatures(data, NETWORK_PARAMETERS); // Assert assertNotNull(result); @@ -186,7 +241,7 @@ void serializeAndDeserializeRskTxsWaitingForSignatures_whenValidEntries_shouldRe byte[] serializedRskTxsWaitingForSignaturesMap = BridgeSerializationUtils.serializeRskTxsWaitingForSignatures(rskTxsWaitingForSignaturesMap); SortedMap deserializedRskTxsWaitingForSignaturesMap = - BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(serializedRskTxsWaitingForSignaturesMap, NETWORK_PARAMETERS, false); + BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(serializedRskTxsWaitingForSignaturesMap, NETWORK_PARAMETERS); // Assert assertNotNull(serializedRskTxsWaitingForSignaturesMap); @@ -201,7 +256,7 @@ void serializeAndDeserializeRskTxsWaitingForSignatures_whenValidEntries_shouldRe void deserializeRskTxsWaitingForSignatures_whenInvalidData_shouldReturnEmptyResult(byte[] data) { // Act SortedMap result = - BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(data, NETWORK_PARAMETERS, false); + BridgeSerializationUtils.deserializeRskTxsWaitingForSignatures(data, NETWORK_PARAMETERS); // Assert assertNotNull(result);