From 8313be3746f9a4f5a02ac4347fa779de6ba67d16 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Fri, 5 Jul 2024 14:35:40 -0400 Subject: [PATCH 1/9] Create BitcoinTransactionBuilder for testing --- .../bitcoin/BtcTransactionBuilder.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java diff --git a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java new file mode 100644 index 00000000..240d88f3 --- /dev/null +++ b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java @@ -0,0 +1,94 @@ +package co.rsk.federate.bitcoin; + +import co.rsk.bitcoinj.core.Address; +import co.rsk.bitcoinj.core.BtcTransaction; +import co.rsk.bitcoinj.core.Coin; +import co.rsk.bitcoinj.core.NetworkParameters; +import co.rsk.bitcoinj.core.TransactionInput; +import co.rsk.bitcoinj.core.TransactionOutPoint; +import co.rsk.bitcoinj.core.TransactionOutput; +import co.rsk.bitcoinj.core.TransactionWitness; +import co.rsk.bitcoinj.script.Script; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class BtcTransactionBuilder { + + private final NetworkParameters networkParameters; + private final Map transactionWitnesses = new HashMap<>(); + private final List transactionInputs = new ArrayList<>(); + private final List transactionOutputs = new ArrayList<>(); + + public BtcTransactionBuilder(NetworkParameters networkParameters) { + this.networkParameters = networkParameters; + } + + public BtcTransactionBuilder addInput(Coin utxoAmount) { + addInput(utxoAmount, null, null); + return this; + } + + public BtcTransactionBuilder addInputWithScriptSig(Coin utxoAmount, Script scriptSig) { + addInput(utxoAmount, scriptSig, null); + return this; + } + + public BtcTransactionBuilder addInputWithWitness(Coin utxoAmount, + TransactionWitness transactionWitness) { + addInput(utxoAmount, null, transactionWitness); + return this; + } + + private void addInput(Coin utxoAmount, Script scriptSig, + TransactionWitness transactionWitness) { + int idx = transactionInputs.size(); + TransactionOutPoint transactionOutpoint = new TransactionOutPoint(networkParameters, idx, + BitcoinTestUtils.createHash(idx)); + TransactionInput transactionInput = new TransactionInput( + networkParameters, null, new byte[]{}, transactionOutpoint, utxoAmount + ); + if (scriptSig != null) { + transactionInput.setScriptSig(scriptSig); + } else if (transactionWitness != null) { + transactionWitnesses.put(idx, transactionWitness); + } + transactionInputs.add(transactionInput); + } + + public BtcTransactionBuilder addInputFrom(TransactionOutput transactionOutput) { + TransactionInput transactionInput = new TransactionInput(networkParameters, null, + new byte[]{}, transactionOutput.getOutPointFor(), transactionOutput.getValue()); + transactionInputs.add(transactionInput); + return this; + } + + public BtcTransactionBuilder addOutput(Coin amount, Address address) { + transactionOutputs.add( + new TransactionOutput(networkParameters, null, amount, address) + ); + return this; + } + + public BtcTransaction build() { + BtcTransaction btcTransaction = new BtcTransaction(networkParameters); + addInputs(btcTransaction); + addOutputs(btcTransaction); + return btcTransaction; + } + + private void addInputs(BtcTransaction btcTransaction) { + transactionInputs.forEach(transactionInput -> { + int idx = btcTransaction.getInputs().size(); + btcTransaction.addInput(transactionInput); + if (transactionWitnesses.containsKey(idx)) { + btcTransaction.setWitness(idx, transactionWitnesses.get(idx)); + } + }); + } + + private void addOutputs(BtcTransaction btcTransaction) { + transactionOutputs.forEach(btcTransaction::addOutput); + } +} From a836393e1f3b530292b6213f50d894f4771702b0 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Mon, 8 Jul 2024 12:12:52 -0400 Subject: [PATCH 2/9] Replace cumbersome pegout creation for the use of BtcTransactionBuilder --- .../client/PowHSMSigningClientBtcTest.java | 63 +++++------ .../PowHSMSignerMessageBuilderTest.java | 103 ++++++++++-------- .../hsm/message/PowHSMSignerMessageTest.java | 50 +++++---- 3 files changed, 113 insertions(+), 103 deletions(-) diff --git a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java index c183a3ed..c1026f45 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java @@ -37,12 +37,12 @@ import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; -import co.rsk.bitcoinj.core.TransactionInput; import co.rsk.bitcoinj.core.TransactionWitness; import co.rsk.bitcoinj.script.Script; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; import co.rsk.federate.bitcoin.BitcoinTestUtils; +import co.rsk.federate.bitcoin.BtcTransactionBuilder; import co.rsk.federate.rpc.JsonRpcClient; import co.rsk.federate.rpc.JsonRpcClientProvider; import co.rsk.federate.rpc.JsonRpcException; @@ -91,7 +91,6 @@ class PowHSMSigningClientBtcTest { bridgeMainnetConstants.getBtcParams(), 9); private static final Federation oldFederation = TestUtils.createFederation( bridgeMainnetConstants.getBtcParams(), 5); - private static final int FIRST_OUTPUT_INDEX = 0; private static final HSMSignature expectedSignature = createMockSignature(); private final ECKey signerPk = ECKey.fromPrivate(Hex.decode("fa01")); @@ -382,10 +381,10 @@ private static List legacyPegoutArgProvider() { for (HSMVersion hsmVersion : powHSMVersions) { // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 arguments.add( - Arguments.of(hsmVersion.getNumber(), coinListOf(50_000_000))); + Arguments.of(hsmVersion, coinListOf(50_000_000))); arguments.add( - Arguments.of(hsmVersion.getNumber(), coinListOf(75_000_000))); - arguments.add(Arguments.of(hsmVersion.getNumber(), + Arguments.of(hsmVersion, coinListOf(75_000_000))); + arguments.add(Arguments.of(hsmVersion, coinListOf(50_000_000, 75_000_000, 100_000_000))); } @@ -399,10 +398,10 @@ private static List signArgProvider() { for (HSMVersion hsmVersion : powHSMVersions) { // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 arguments.add( - Arguments.of(hsmVersion.getNumber(), Hex.decode("FE80F0FA02"), coinListOf(50_000_000))); + Arguments.of(hsmVersion, Hex.decode("FE80F0FA02"), coinListOf(50_000_000))); arguments.add( - Arguments.of(hsmVersion.getNumber(), Hex.decode("FEC0687804"), coinListOf(75_000_000))); - arguments.add(Arguments.of(hsmVersion.getNumber(), Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), + Arguments.of(hsmVersion, Hex.decode("FEC0687804"), coinListOf(75_000_000))); + arguments.add(Arguments.of(hsmVersion, Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), coinListOf(50_000_000, 75_000_000, 100_000_000))); } @@ -423,36 +422,38 @@ private void addCommonPegoutLogs(List logs, BtcTransaction pegoutBtcTx) private BtcTransaction createPegout(List outpointValues, List
destinationAddresses, boolean segwit) { - BtcTransaction fundingTransaction = new BtcTransaction(btcMainnetParams); - fundingTransaction.addInput(BitcoinTestUtils.createHash(1), FIRST_OUTPUT_INDEX, - new Script(new byte[]{})); - for (Coin outpointValue : outpointValues) { - fundingTransaction.addOutput(outpointValue, oldFederation.getAddress()); - } - - BtcTransaction pegoutBtcTx = new BtcTransaction(btcMainnetParams); + BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); + // TODO: improve this method to create a more realistic btc segwit transaction + // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit Script inputScriptThatSpendsFromTheFederation = createBaseInputScriptThatSpendsFromTheFederation( oldFederation); Coin fee = Coin.MILLICOIN; - for (int outputIndex = 0; outputIndex < outpointValues.size(); outputIndex++) { - TransactionInput addedInput = pegoutBtcTx.addInput( - fundingTransaction.getOutput(outputIndex)); + for (int idx = 0; idx < outpointValues.size(); idx++) { + Coin outpointValue = outpointValues.get(idx); + Coin amountToSend = outpointValue.minus(fee); + Address destinationAddress = destinationAddresses.get( + idx % destinationAddresses.size()); + + btcTransactionBuilder.addInputWithScriptSig(outpointValue, + inputScriptThatSpendsFromTheFederation); + // Iterate over the addresses using idx % addresses.size() to have outputs to different addresses + btcTransactionBuilder.addOutput(amountToSend, destinationAddress); + } - addedInput.setScriptSig(inputScriptThatSpendsFromTheFederation); + BtcTransaction btcTransaction = btcTransactionBuilder.build(); - if (segwit) { - addWitness(pegoutBtcTx, outputIndex); - } + // make the tx segwit by adding a single witness + if (segwit) { + TransactionWitness witness = new TransactionWitness(1); + witness.setPush(0, new byte[]{1}); - Coin amountToSend = outpointValues.get(outputIndex).minus(fee); - pegoutBtcTx.addOutput(amountToSend, - destinationAddresses.get(outputIndex % destinationAddresses.size())); + int fistInputIdx = 0; + btcTransaction.setWitness(fistInputIdx, witness); } - - return pegoutBtcTx; + return btcTransaction; } private List
createDestinationAddresses(int numberOfAddresses) { @@ -462,12 +463,6 @@ private List
createDestinationAddresses(int numberOfAddresses) { .collect(Collectors.toList()); } - private void addWitness(BtcTransaction pegoutBtcTx, int inputIndex) { - TransactionWitness txWitness = new TransactionWitness(1); - txWitness.setPush(0, new byte[]{0x1}); - pegoutBtcTx.setWitness(inputIndex, txWitness); - } - private void signAndExecuteAssertions(int hsmVersion, List expectedOutpointValues, PowHSMSignerMessageBuilder powHSMSignerMessageBuilder) throws JsonRpcException, SignerMessageBuilderException, HSMClientException { diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java index 7ed9c8c8..b717ad20 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java @@ -16,19 +16,17 @@ import static org.mockito.Mockito.when; import co.rsk.bitcoinj.core.Address; -import co.rsk.bitcoinj.core.BtcECKey; import co.rsk.bitcoinj.core.BtcTransaction; import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; -import co.rsk.bitcoinj.core.TransactionInput; import co.rsk.bitcoinj.core.TransactionWitness; -import co.rsk.bitcoinj.crypto.TransactionSignature; import co.rsk.bitcoinj.script.Script; import co.rsk.core.RskAddress; import co.rsk.core.bc.BlockHashesHelper; import co.rsk.crypto.Keccak256; import co.rsk.federate.bitcoin.BitcoinTestUtils; +import co.rsk.federate.bitcoin.BtcTransactionBuilder; import co.rsk.federate.signing.utils.TestUtils; import co.rsk.peg.constants.BridgeConstants; import co.rsk.peg.constants.BridgeMainNetConstants; @@ -36,6 +34,7 @@ import co.rsk.peg.pegin.RejectedPeginReason; import co.rsk.trie.Trie; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -59,8 +58,6 @@ class PowHSMSignerMessageBuilderTest { - private static final int FIRST_OUTPUT_INDEX = 0; - private static final BridgeConstants bridgeMainnetConstants = BridgeMainNetConstants.getInstance(); private static final NetworkParameters btcMainnetParams = bridgeMainnetConstants.getBtcParams(); @@ -70,13 +67,24 @@ class PowHSMSignerMessageBuilderTest { bridgeMainnetConstants.getBtcParams(), 9); private static final Federation oldFederation = TestUtils.createFederation( bridgeMainnetConstants.getBtcParams(), 9); - private final BtcECKey signerBtcPk = BtcECKey.fromPrivate(Hex.decode("fa01")); + private Transaction pegoutCreationRskTx; private Transaction pegoutConfirmationRskTx; private Block pegoutCreationBlock; private TransactionReceipt pegoutCreationRskTxReceipt; private ReceiptStore receiptStore; + private static List serializedAndDeserializedOutpointValuesArgProvider() { + List arguments = new ArrayList<>(); + // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 + arguments.add(Arguments.of(Hex.decode("FE80F0FA02"), coinListOf(50_000_000))); + arguments.add(Arguments.of(Hex.decode("FEC0687804"), coinListOf(75_000_000))); + arguments.add(Arguments.of(Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), + coinListOf(50_000_000, 75_000_000, 100_000_000))); + + return arguments; + } + @BeforeEach void setUp() { Keccak256 pegoutCreationRskTxHash = TestUtils.createHash(2); @@ -140,7 +148,8 @@ void createHSMVersion2Message() throws SignerMessageBuilderException { Optional.of(txInfo)); List outpointValues = Collections.singletonList(Coin.COIN); - BtcTransaction pegoutBtcTx = createPegout(outpointValues, userAddress, false); + BtcTransaction pegoutBtcTx = createPegout(outpointValues, + Collections.singletonList(userAddress), false); int inputIndex = 0; //Act @@ -199,8 +208,13 @@ void buildMessageForIndex_fails() { void buildMessageForIndex_whenLegacyBatchPegoutHasTransactionCreatedEvent_ok() throws SignerMessageBuilderException { // arrange - List outpointValues = Collections.singletonList(Coin.COIN); - BtcTransaction pegoutBtcTx = createPegout(outpointValues, userAddress, false); + Address userAddress2 = BitcoinTestUtils.createP2PKHAddress(btcMainnetParams, + "userAddress2"); + Coin minimumPegoutTxValue = bridgeMainnetConstants.getMinimumPegoutTxValue(); + List outpointValues = Arrays.asList(Coin.COIN, minimumPegoutTxValue, + minimumPegoutTxValue); + BtcTransaction pegoutBtcTx = createPegout(outpointValues, + Arrays.asList(userAddress, userAddress2), false); List logs = new ArrayList<>(); @@ -270,7 +284,11 @@ void buildMessageForIndex_whenSegwitBatchPegoutHasTransactionCreatedEvent_ok( byte[] serializedOutpointValues, List expectedOutpointValues) throws SignerMessageBuilderException { // arrange - BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, userAddress, true); + Address userAddress2 = BitcoinTestUtils.createP2PKHAddress(btcMainnetParams, + "userAddress2"); + List
destinationAddresses = Arrays.asList(userAddress, userAddress2); + BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, destinationAddresses, + true); List logs = new ArrayList<>(); @@ -298,7 +316,7 @@ void buildMessageForIndex_whenSegwitMigrationPegoutHasTransactionCreatedEvent_ok throws SignerMessageBuilderException { // arrange BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, - newFederation.getAddress(), true); + Collections.singletonList(newFederation.getAddress()), true); List logs = new ArrayList<>(); @@ -319,7 +337,8 @@ void buildMessageForIndex_whenRejectedPeginHasTransactionCreatedEvent_ok( byte[] serializedOutpointValues, List expectedOutpointValues) throws SignerMessageBuilderException { // arrange - BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, userAddress, true); + BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, + Collections.singletonList(userAddress), true); List logs = new ArrayList<>(); @@ -341,17 +360,6 @@ void buildMessageForIndex_whenRejectedPeginHasTransactionCreatedEvent_ok( buildMessageForIndexAndExecuteAssertions(expectedOutpointValues, pegoutBtcTx); } - private static List serializedAndDeserializedOutpointValuesArgProvider() { - List arguments = new ArrayList<>(); - // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 - arguments.add(Arguments.of(Hex.decode("FE80F0FA02"), coinListOf(50_000_000))); - arguments.add(Arguments.of(Hex.decode("FEC0687804"), coinListOf(75_000_000))); - arguments.add(Arguments.of(Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), - coinListOf(50_000_000, 75_000_000, 100_000_000))); - - return arguments; - } - private void buildMessageForIndexAndExecuteAssertions(List expectedOutpointValues, BtcTransaction pegoutBtcTx) throws SignerMessageBuilderException { @@ -397,37 +405,40 @@ private void assertSignerMessage(SignerMessage actualSignerMessage, BtcTransacti actualPowHSMSignerMessage.getReceiptMerkleProof()); } - private BtcTransaction createPegout(List outpointValues, Address destinationAddress, - boolean segwit) { - BtcTransaction fundingTransaction = new BtcTransaction(btcMainnetParams); - fundingTransaction.addInput(BitcoinTestUtils.createHash(1), FIRST_OUTPUT_INDEX, - new Script(new byte[]{})); - - for (Coin outpointValue : outpointValues) { - fundingTransaction.addOutput(outpointValue, oldFederation.getAddress()); - } + private BtcTransaction createPegout(List outpointValues, + List
destinationAddresses, boolean segwit) { - BtcTransaction pegoutBtcTx = new BtcTransaction(btcMainnetParams); + BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); + // TODO: improve this method to create a more realistic btc segwit transaction + // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit Script inputScriptThatSpendsFromTheFederation = createBaseInputScriptThatSpendsFromTheFederation( oldFederation); Coin fee = Coin.MILLICOIN; - for (int inputIndex = 0; inputIndex < outpointValues.size(); inputIndex++) { - TransactionInput addedInput = pegoutBtcTx.addInput( - fundingTransaction.getOutput(inputIndex)); - addedInput.setScriptSig(inputScriptThatSpendsFromTheFederation); - if (segwit) { - TransactionWitness transactionWitness = TransactionWitness.createWitness( - TransactionSignature.dummy(), signerBtcPk); - pegoutBtcTx.setWitness(inputIndex, transactionWitness); - } - - Coin amountToSend = outpointValues.get(inputIndex).minus(fee); - pegoutBtcTx.addOutput(amountToSend, destinationAddress); + for (int idx = 0; idx < outpointValues.size(); idx++) { + Coin outpointValue = outpointValues.get(idx); + Coin amountToSend = outpointValue.minus(fee); + Address destinationAddress = destinationAddresses.get( + idx % destinationAddresses.size()); + + btcTransactionBuilder.addInputWithScriptSig(outpointValue, + inputScriptThatSpendsFromTheFederation); + // Iterate over the addresses using idx % addresses.size() to have outputs to different addresses + btcTransactionBuilder.addOutput(amountToSend, destinationAddress); } - return pegoutBtcTx; + BtcTransaction btcTransaction = btcTransactionBuilder.build(); + + // make the tx segwit by adding a single witness + if (segwit) { + TransactionWitness witness = new TransactionWitness(1); + witness.setPush(0, new byte[]{1}); + + int fistInputIdx = 0; + btcTransaction.setWitness(fistInputIdx, witness); + } + return btcTransaction; } private void addCommonPegoutLogs(List logs, BtcTransaction pegoutBtcTx) { diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java index 329d00d2..d1a0e36d 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java @@ -24,6 +24,7 @@ import static co.rsk.federate.signing.HSMField.SIGHASH_COMPUTATION_MODE; import static co.rsk.federate.signing.HSMField.TX; import static co.rsk.federate.signing.HSMField.WITNESS_SCRIPT; +import static co.rsk.federate.signing.utils.TestUtils.createBaseInputScriptThatSpendsFromTheFederation; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -32,17 +33,16 @@ import static org.mockito.Mockito.when; import co.rsk.bitcoinj.core.Address; -import co.rsk.bitcoinj.core.BtcECKey; import co.rsk.bitcoinj.core.BtcTransaction; import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; import co.rsk.bitcoinj.core.TransactionWitness; -import co.rsk.bitcoinj.crypto.TransactionSignature; import co.rsk.bitcoinj.params.RegTestParams; import co.rsk.bitcoinj.script.Script; import co.rsk.crypto.Keccak256; import co.rsk.federate.bitcoin.BitcoinTestUtils; +import co.rsk.federate.bitcoin.BtcTransactionBuilder; import co.rsk.federate.signing.utils.TestUtils; import co.rsk.peg.constants.BridgeConstants; import co.rsk.peg.constants.BridgeMainNetConstants; @@ -80,8 +80,6 @@ class PowHSMSignerMessageTest { private static final String SIMPLE_RAW_BTC_TX = "020000000001017001d967a340069c0b169fcbeb9cb6e0d78a27c94a41acbce762abc695aefab10000000017160014cfa63de9979e2a8005e6cb516b86202860ff3971ffffffff0200c2eb0b0000000017a914291a7ddc558810708149a731f39cd3c3a8782cfd870896e1110000000017a91425a2e67511a0207c4387ce8d3eeef498a4782e64870247304402207e0615f440bbc50351fb5d8839b3fae6c74f652c9ffc9291008f4ea39f9565980220354c734511a0560367b300eecb1a7472317a995462622e06ee91cbe0517c17e1012102e87cd90f3cb0d64eeba797fbb8f8ceaadc09e0128afbaefb0ee9535875ea395400000000"; private final List noOutpointValuesForLegacyPegouts = Collections.emptyList(); - private final BtcECKey signerBtcPk = BtcECKey.fromPrivate(Hex.decode("fa01")); - @Test void equality() { String anotherRawBtcTx = "0200000001ba47c93a9c8bac4a305ca5e0f8a9a81d1972e06c20fdb0f62ac9960bd48529eb000000006a47304402207ce2c6749019d78c9e7a39a82a08b36956fd05edbb8e820eef5946dc6008c5d00220024a1b6261a8afd9ad8c8888d5fb8f9bc75bc273100abaa68dac225fadcf2c82012103efa4762ccc1358b72f597d002b7fd1cd58cd05db34fe9fa63e43634acf200927ffffffff0200c2eb0b000000001976a914c4f8ff441f41bcc771519dc5ceb9e5a1ee058f7b88ac6cb0eb0b000000001976a9146a6ba0b567d75a449d158fe2d3dacc53f58b246688ac00000000"; @@ -286,32 +284,38 @@ void getMessageToSign_whenSighashSegwitMode_ok() { } private BtcTransaction createSegwitPegout(List outpointValues) { - BtcTransaction fundingTransaction = new BtcTransaction(btcMainnetParams); - int utxoOutputIndex = 0; - fundingTransaction.addInput(BitcoinTestUtils.createHash(1), utxoOutputIndex, - new Script(new byte[]{})); - - for (Coin outpointValue : outpointValues) { - fundingTransaction.addOutput(outpointValue, activeFederation.getAddress()); - } + BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); - BtcTransaction segwitPegoutBtcTx = new BtcTransaction(btcMainnetParams); + // TODO: improve this method to create a more realistic btc segwit transaction + // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit + Script inputScriptThatSpendsFromTheFederation = createBaseInputScriptThatSpendsFromTheFederation( + activeFederation); + Address userAddress2 = BitcoinTestUtils.createP2PKHAddress(btcMainnetParams, + "userAddress"); + List
destinationAddresses = Arrays.asList(userAddress, userAddress2); Coin fee = Coin.MILLICOIN; + for (int idx = 0; idx < outpointValues.size(); idx++) { + Coin outpointValue = outpointValues.get(idx); + Coin amountToSend = outpointValue.minus(fee); + Address destinationAddress = destinationAddresses.get( + idx % destinationAddresses.size()); - for (int i = 0; i < outpointValues.size(); i++) { - Coin outpointValue = outpointValues.get(i); + btcTransactionBuilder.addInputWithScriptSig(outpointValue, + inputScriptThatSpendsFromTheFederation); + // Iterate over the addresses using idx % addresses.size() to have outputs to different addresses + btcTransactionBuilder.addOutput(amountToSend, destinationAddress); + } - segwitPegoutBtcTx.addInput(fundingTransaction.getOutput(i)); + BtcTransaction btcTransaction = btcTransactionBuilder.build(); - TransactionWitness transactionWitness = TransactionWitness.createWitness( - TransactionSignature.dummy(), signerBtcPk); - segwitPegoutBtcTx.setWitness(i, transactionWitness); + // make the tx segwit by adding a single witness + TransactionWitness witness = new TransactionWitness(1); + witness.setPush(0, new byte[]{1}); - Coin amountToSend = outpointValue.minus(fee); - segwitPegoutBtcTx.addOutput(amountToSend, userAddress); - } - return segwitPegoutBtcTx; + int fistInputIdx = 0; + btcTransaction.setWitness(fistInputIdx, witness); + return btcTransaction; } private void assertHsmMessageValues(BtcTransaction segwitPegoutBtcTx, From 53ebf6f350fc81b07cc0983f6fba5bfd40c3f7c1 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Mon, 8 Jul 2024 12:31:34 -0400 Subject: [PATCH 3/9] Move test setup to instance variables then do the setup in the beforeTest method --- .../hsm/message/PowHSMSignerMessageTest.java | 63 ++++++------------- 1 file changed, 19 insertions(+), 44 deletions(-) diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java index d1a0e36d..f61d85a1 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java @@ -29,9 +29,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - import co.rsk.bitcoinj.core.Address; import co.rsk.bitcoinj.core.BtcTransaction; import co.rsk.bitcoinj.core.Coin; @@ -40,7 +37,6 @@ import co.rsk.bitcoinj.core.TransactionWitness; import co.rsk.bitcoinj.params.RegTestParams; import co.rsk.bitcoinj.script.Script; -import co.rsk.crypto.Keccak256; import co.rsk.federate.bitcoin.BitcoinTestUtils; import co.rsk.federate.bitcoin.BtcTransactionBuilder; import co.rsk.federate.signing.utils.TestUtils; @@ -56,9 +52,9 @@ import java.util.Collections; import java.util.List; import org.bouncycastle.util.encoders.Hex; -import org.ethereum.core.BlockHeader; import org.ethereum.core.TransactionReceipt; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -72,13 +68,21 @@ class PowHSMSignerMessageTest { private static final NetworkParameters btcMainnetParams = bridgeMainnetConstants.getBtcParams(); private static final Address userAddress = BitcoinTestUtils.createP2PKHAddress(btcMainnetParams, "userAddress"); - private static final Federation activeFederation = TestUtils.createFederation( bridgeMainnetConstants.getBtcParams(), 9); private static final int FIRST_INPUT_INDEX = 0; private static final String ENCODED_PEGOUT_BTC_TX = "02000000035f4703109ad9353cd98456ab9408ba985344751e1a11af4bb529b469dee71e3901000000fd390300483045022100f12d0050477bfcb399c640aa5f756cbf24516c73b8b8a285a89d00a3d0adfac802204af80403b8523162c01830622ba75ad59304f88a727056cd4ede83703dee690d01483045022100cf03b9dad96a6d6f5d2d1412b05d7c4fcaa56496363d14b58c7c865ae75e05e40220540e56447278c6bdde1cd056c1376df5f099e854ba06f9241a39491f3af85d50014730440220630131c89d2548b5dd651e03d2e8d3cbbca0faaef59e5f841be21b89b8e26be8022056c9097785af0c3cdab5d0d6e2200668e3638a19b6395243680a91201dd6486b01483045022100a8c0b546f15339fa6dfcd3706e4b36a217db499bb6bdae7a8eeee1e4b17bc7ca022012f3eb64acf1d06729cc4f249e1de6766028cee83f32a0952d53c06df1410eea01473044022008c8b525aa12769538426f816b11e8fc05cc9299bee50b95831e6bbb9a9ebf21022055f043ca22872c21b3b8861f4e951e579b38c515c663773029665cfb2f63d7f601004dc901645521020ace50bab1230f8002a0bfe619482af74b338cc9e4c956add228df47e6adae1c21025093f439fb8006fd29ab56605ffec9cdc840d16d2361004e1337a2f86d8bd2db210275d473555de2733c47125f9702b0f870df1d817379f5587f09b6c40ed2c6c9492102a95f095d0ce8cb3b9bf70cc837e3ebe1d107959b1fa3f9b2d8f33446f9c8cbdb2103250c11be0561b1d7ae168b1f59e39cbc1fd1ba3cf4d2140c1a365b2723a2bf9321034851379ec6b8a701bd3eef8a0e2b119abb4bdde7532a3d6bcbff291b0daf3f25210350179f143a632ce4e6ac9a755b82f7f4266cfebb116a42cadb104c2c2a3350f92103b04fbd87ef5e2c0946a684c8c93950301a45943bbe56d979602038698facf9032103b58a5da144f5abab2e03e414ad044b732300de52fa25c672a7f7b3588877190659ae670350cd00b275532102370a9838e4d15708ad14a104ee5606b36caaaaf739d833e67770ce9fd9b3ec80210257c293086c4d4fe8943deda5f890a37d11bebd140e220faa76258a41d077b4d42103c2660a46aa73078ee6016dee953488566426cf55fc8011edd0085634d75395f92103cd3e383ec6e12719a6c69515e5559bcbe037d0aa24c187e1e26ce932e22ad7b354ae68ffffffff520d1672226aca6881bef455859e29fc818002b8f34357b371e05a8fa5aa0a4e01000000fd38030047304402201eef91a4f0c4f1c1ee01bf933cab816f4fef78df50b0cb37f34c112a75a17a530220488380eabaad1092d797b5cfcde43766663a7a28c6a6e2cceff2f611891c710f01483045022100e77fea1592a20c22b37d579e6e7a9a0b5533d407a6e6da87335570405b99aba702205976ca34f76363a3551b2062444773cd09349815b8b600db13c1be345fa1753601483045022100debdabd74fd654431af3bb210ef366e2b60e6256bdc7b668e0433ac5f0b1af12022018c3af35a6beb03b89cdd30acb62cb33f7932b7f36783b42c8056d8580b6109401473044022050cc3bc452de349e585d38d1c56c8cfe4aeb22d517b80d690c8849802547ed98022040afaf09eb05bca842cd24e9a0274ce68a2bbef256e5a26c1a2293a16c5c9d7d0147304402207ffca8d759d8fad9c707afc47d0dbf83a892703a4f4c3ad97d81144015028a940220713cf28b4ca2210782d95a2b8241d706109b8624602dcbfa34bc7799200ca3d201004dc901645521020ace50bab1230f8002a0bfe619482af74b338cc9e4c956add228df47e6adae1c21025093f439fb8006fd29ab56605ffec9cdc840d16d2361004e1337a2f86d8bd2db210275d473555de2733c47125f9702b0f870df1d817379f5587f09b6c40ed2c6c9492102a95f095d0ce8cb3b9bf70cc837e3ebe1d107959b1fa3f9b2d8f33446f9c8cbdb2103250c11be0561b1d7ae168b1f59e39cbc1fd1ba3cf4d2140c1a365b2723a2bf9321034851379ec6b8a701bd3eef8a0e2b119abb4bdde7532a3d6bcbff291b0daf3f25210350179f143a632ce4e6ac9a755b82f7f4266cfebb116a42cadb104c2c2a3350f92103b04fbd87ef5e2c0946a684c8c93950301a45943bbe56d979602038698facf9032103b58a5da144f5abab2e03e414ad044b732300de52fa25c672a7f7b3588877190659ae670350cd00b275532102370a9838e4d15708ad14a104ee5606b36caaaaf739d833e67770ce9fd9b3ec80210257c293086c4d4fe8943deda5f890a37d11bebd140e220faa76258a41d077b4d42103c2660a46aa73078ee6016dee953488566426cf55fc8011edd0085634d75395f92103cd3e383ec6e12719a6c69515e5559bcbe037d0aa24c187e1e26ce932e22ad7b354ae68ffffffffe6396135c228001074a16853ca4d2e990ed0d6a6c2862943684b17236418b07a01000000fd37030047304402205eb742664c3227ef1c076dc1d59ef6fc6f988317bf54b07bfd8f5804cccb6e8802207dd16a91cdcd35bb086023f0548c2b4717274d6b6e512b3983da7f43c497b93001473044022020cdb90e2f3b44624cc7a2894d050b97916699a4c3080f474860a7de5b08d05a0220265eb0e1d7206b7d349803cd38bd85e1c4bcc1d515b078f85540951ccafba1ec01473044022045a5ad4888d85e837e44c93d4d55719db16d7f161d1d52fc1a8553f7c59108d1022020bfe8924f9d8660c9d9cf34eedf940cd56e5bb748df3c2005d1bdbb78f243970147304402203daaa315fe50490edc1165e324e894ffd395315ec20ba5fdd9399e6aa186d447022064dd19c85db2c26092ab6373712ea3700df90ddeac345666c0cd363c0ac09b8101483045022100e83a4649a5118fac945c1029945b115f019dd4b0b9958842adc86d43a8e624580220587c5109435cba845ea2303988796b1d8aa427dd3f3e08222f8ddec446d72c9001004dc901645521020ace50bab1230f8002a0bfe619482af74b338cc9e4c956add228df47e6adae1c21025093f439fb8006fd29ab56605ffec9cdc840d16d2361004e1337a2f86d8bd2db210275d473555de2733c47125f9702b0f870df1d817379f5587f09b6c40ed2c6c9492102a95f095d0ce8cb3b9bf70cc837e3ebe1d107959b1fa3f9b2d8f33446f9c8cbdb2103250c11be0561b1d7ae168b1f59e39cbc1fd1ba3cf4d2140c1a365b2723a2bf9321034851379ec6b8a701bd3eef8a0e2b119abb4bdde7532a3d6bcbff291b0daf3f25210350179f143a632ce4e6ac9a755b82f7f4266cfebb116a42cadb104c2c2a3350f92103b04fbd87ef5e2c0946a684c8c93950301a45943bbe56d979602038698facf9032103b58a5da144f5abab2e03e414ad044b732300de52fa25c672a7f7b3588877190659ae670350cd00b275532102370a9838e4d15708ad14a104ee5606b36caaaaf739d833e67770ce9fd9b3ec80210257c293086c4d4fe8943deda5f890a37d11bebd140e220faa76258a41d077b4d42103c2660a46aa73078ee6016dee953488566426cf55fc8011edd0085634d75395f92103cd3e383ec6e12719a6c69515e5559bcbe037d0aa24c187e1e26ce932e22ad7b354ae68ffffffff0366bd6aee000000001976a91473179e1c334c7b9c393f3a31d6e9c1896bf480cd88ac3ac13001000000001976a91480befab786627776d2bd50394abdd8623c0f039c88ac60911bbb0200000017a914d3530b561910c250f58fbd572d2f7a7d847354ef8700000000"; private static final String SIMPLE_RAW_BTC_TX = "020000000001017001d967a340069c0b169fcbeb9cb6e0d78a27c94a41acbce762abc695aefab10000000017160014cfa63de9979e2a8005e6cb516b86202860ff3971ffffffff0200c2eb0b0000000017a914291a7ddc558810708149a731f39cd3c3a8782cfd870896e1110000000017a91425a2e67511a0207c4387ce8d3eeef498a4782e64870247304402207e0615f440bbc50351fb5d8839b3fae6c74f652c9ffc9291008f4ea39f9565980220354c734511a0560367b300eecb1a7472317a995462622e06ee91cbe0517c17e1012102e87cd90f3cb0d64eeba797fbb8f8ceaadc09e0128afbaefb0ee9535875ea395400000000"; private final List noOutpointValuesForLegacyPegouts = Collections.emptyList(); + private TransactionReceipt txReceipt; + private List receiptMerkleProof; + + @BeforeEach + void setUp() { + txReceipt = new TransactionReceipt(); + receiptMerkleProof = new ArrayList<>(); + receiptMerkleProof.add(new Trie()); + } @Test void equality() { @@ -88,22 +92,15 @@ void equality() { SIMPLE_RAW_BTC_TX)); BtcTransaction btcTx2 = new BtcTransaction(RegTestParams.get(), Hex.decode(anotherRawBtcTx)); - int inputIndex = 0; - byte[] bytes = new byte[32]; - bytes[0] = (byte) 1; - BlockHeader blockHeader = mock(BlockHeader.class); - when(blockHeader.getHash()).thenReturn(new Keccak256(bytes)); - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); + Sha256Hash sigHash = Sha256Hash.wrap( "0000000000000000000000000000000000000000000000000000000000000001"); - SignerMessage m1 = new PowHSMSignerMessage(btcTx1, inputIndex, txReceipt, + SignerMessage m1 = new PowHSMSignerMessage(btcTx1, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); - SignerMessage m2 = new PowHSMSignerMessage(btcTx1, inputIndex, txReceipt, + SignerMessage m2 = new PowHSMSignerMessage(btcTx1, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); - SignerMessage m3 = new PowHSMSignerMessage(btcTx2, inputIndex, txReceipt, + SignerMessage m3 = new PowHSMSignerMessage(btcTx2, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); assertEquals(m1, m2); @@ -119,13 +116,9 @@ void equality() { void getBtcTransactionSerialized() { BtcTransaction btcTx = new BtcTransaction(RegTestParams.get(), Hex.decode(SIMPLE_RAW_BTC_TX)); - int inputIndex = 0; - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); Sha256Hash sigHash = Sha256Hash.ZERO_HASH; - PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, inputIndex, txReceipt, + PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); String txSerialized = message.getBtcTransactionSerialized(); @@ -138,9 +131,6 @@ void getInputIndex() { BtcTransaction btcTx = new BtcTransaction(RegTestParams.get(), Hex.decode(SIMPLE_RAW_BTC_TX)); int inputIndex = 2; - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); Sha256Hash sigHash = Sha256Hash.ZERO_HASH; PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, inputIndex, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); @@ -151,13 +141,9 @@ void getInputIndex() { @Test void getTransactionReceiptReceipt() { BtcTransaction btcTx = new BtcTransaction(RegTestParams.get(), Hex.decode(SIMPLE_RAW_BTC_TX)); - int inputIndex = 0; - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); Sha256Hash sigHash = Sha256Hash.ZERO_HASH; - PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, inputIndex, txReceipt, + PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); String receipt = message.getTransactionReceipt(); @@ -168,14 +154,9 @@ void getTransactionReceiptReceipt() { @Test void getReceiptMerkleProof() { BtcTransaction btcTx = new BtcTransaction(RegTestParams.get(), Hex.decode(SIMPLE_RAW_BTC_TX)); - int inputIndex = 0; - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); - receiptMerkleProof.add(new Trie()); Sha256Hash sigHash = Sha256Hash.ZERO_HASH; - PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, inputIndex, txReceipt, + PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); String[] encodedReceipts = new String[receiptMerkleProof.size()]; @@ -192,13 +173,10 @@ void getReceiptMerkleProof() { @Test void getSigHash() { BtcTransaction btcTx = new BtcTransaction(RegTestParams.get(), Hex.decode(SIMPLE_RAW_BTC_TX)); - int inputIndex = 0; - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); + Sha256Hash sigHash = Sha256Hash.wrap( "0000000000000000000000000000000000000000000000000000000000000001"); - PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, inputIndex, txReceipt, + PowHSMSignerMessage message = new PowHSMSignerMessage(btcTx, FIRST_INPUT_INDEX, txReceipt, receiptMerkleProof, sigHash, noOutpointValuesForLegacyPegouts); assertEquals(sigHash, message.getSigHash()); @@ -208,9 +186,6 @@ void getSigHash() { @MethodSource("getMessageToSignLegacyArgProvider") void getMessageToSign_whenSighashLegacyMode_ok(int version, JsonNode expectedMessageToSend) { // arrange - TransactionReceipt txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); - receiptMerkleProof.add(new Trie()); Sha256Hash sigHash = BitcoinTestUtils.createHash(1); BtcTransaction pegoutBtcTx = new BtcTransaction(btcMainnetParams, From 9c5b3f06d30fac8b61cc87156e28a469dfefa577 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Mon, 8 Jul 2024 14:09:59 -0400 Subject: [PATCH 4/9] Move test setup to instance variables then do the setup in the beforeTest method --- .../PowHSMSignerMessageBuilderTest.java | 125 +++++++----------- 1 file changed, 49 insertions(+), 76 deletions(-) diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java index b717ad20..517db2a3 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java @@ -9,9 +9,7 @@ import static co.rsk.federate.signing.utils.TestUtils.createBaseInputScriptThatSpendsFromTheFederation; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -74,17 +72,6 @@ class PowHSMSignerMessageBuilderTest { private TransactionReceipt pegoutCreationRskTxReceipt; private ReceiptStore receiptStore; - private static List serializedAndDeserializedOutpointValuesArgProvider() { - List arguments = new ArrayList<>(); - // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 - arguments.add(Arguments.of(Hex.decode("FE80F0FA02"), coinListOf(50_000_000))); - arguments.add(Arguments.of(Hex.decode("FEC0687804"), coinListOf(75_000_000))); - arguments.add(Arguments.of(Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), - coinListOf(50_000_000, 75_000_000, 100_000_000))); - - return arguments; - } - @BeforeEach void setUp() { Keccak256 pegoutCreationRskTxHash = TestUtils.createHash(2); @@ -126,27 +113,6 @@ private Block createBlock(int blockNumber, List rskTxs) { @Test void createHSMVersion2Message() throws SignerMessageBuilderException { //Arrange - Transaction rskTx = mock(Transaction.class); - Keccak256 rskTxHash = Keccak256.ZERO_HASH; - when(rskTx.getHash()).thenReturn(rskTxHash); - - byte[] rskBlockHash = new byte[]{0x2}; - - TransactionReceipt txReceipt = new TransactionReceipt(); - txReceipt.setTransaction(rskTx); - TransactionInfo txInfo = new TransactionInfo(txReceipt, rskBlockHash, 0); - - BlockHeader blockHeader = mock(BlockHeader.class); - when(blockHeader.getHash()).thenReturn(Keccak256.ZERO_HASH); - byte[] trieRoot = BlockHashesHelper.getTxTrieRoot(Collections.singletonList(rskTx), true); - when(blockHeader.getTxTrieRoot()).thenReturn(trieRoot); - - Block block = new Block(blockHeader, Collections.singletonList(rskTx), - Collections.emptyList(), true, true); - - when(receiptStore.get(rskTxHash.getBytes(), Keccak256.ZERO_HASH.getBytes())).thenReturn( - Optional.of(txInfo)); - List outpointValues = Collections.singletonList(Coin.COIN); BtcTransaction pegoutBtcTx = createPegout(outpointValues, Collections.singletonList(userAddress), false); @@ -154,54 +120,50 @@ void createHSMVersion2Message() throws SignerMessageBuilderException { //Act PowHSMSignerMessageBuilder sigMessVersion2 = new PowHSMSignerMessageBuilder(receiptStore, - new ReleaseCreationInformation(block, txReceipt, rskTxHash, pegoutBtcTx, rskTxHash)); - PowHSMSignerMessage message = (PowHSMSignerMessage) sigMessVersion2.buildMessageForIndex( + new ReleaseCreationInformation(pegoutCreationBlock, pegoutCreationRskTxReceipt, + pegoutCreationRskTx.getHash(), pegoutBtcTx, pegoutConfirmationRskTx.getHash())); + PowHSMSignerMessage actualPowHSMSignerMessage = (PowHSMSignerMessage) sigMessVersion2.buildMessageForIndex( inputIndex); - PowHSMSignerMessage message2 = (PowHSMSignerMessage) sigMessVersion2.buildMessageForIndex( + PowHSMSignerMessage actualPowHSMSignerMessage2 = (PowHSMSignerMessage) sigMessVersion2.buildMessageForIndex( inputIndex); //Assert - List receiptMerkleProof = BlockHashesHelper.calculateReceiptsTrieRootFor(block, - receiptStore, rskTxHash); + int actualInputIndex = actualPowHSMSignerMessage.getInputIndex(); + assertEquals(inputIndex, actualInputIndex); - assertNotNull(receiptMerkleProof); - String[] encodedReceipts = new String[receiptMerkleProof.size()]; - for (int i = 0; i < encodedReceipts.length; i++) { - encodedReceipts[i] = Hex.toHexString(receiptMerkleProof.get(i).toMessage()); - } - Sha256Hash sigHash = pegoutBtcTx.hashForSignature(0, oldFederation.getRedeemScript(), - BtcTransaction.SigHash.ALL, false); - - assertEquals(Hex.toHexString(pegoutBtcTx.bitcoinSerialize()), - message.getBtcTransactionSerialized()); - assertEquals(inputIndex, message.getInputIndex()); - assertEquals(Hex.toHexString(txReceipt.getEncoded()), message.getTransactionReceipt()); - assertArrayEquals(encodedReceipts, message.getReceiptMerkleProof()); - assertEquals(sigHash, message.getSigHash()); - // Building message twice returns same message - assertEquals(message, message2); + Sha256Hash expectedSigHash = pegoutBtcTx.hashForSignature(inputIndex, + oldFederation.getRedeemScript(), BtcTransaction.SigHash.ALL, false); + assertEquals(expectedSigHash, actualPowHSMSignerMessage.getSigHash()); + + String expectedBtcTxSerialized = Hex.toHexString(pegoutBtcTx.bitcoinSerialize()); + String actualBtcTxSerialized = actualPowHSMSignerMessage.getBtcTransactionSerialized(); + assertEquals(expectedBtcTxSerialized, actualBtcTxSerialized); + + String[] expectedReceiptMerkleProof = getEncodedReceiptMerkleProof(receiptStore); + assertArrayEquals(expectedReceiptMerkleProof, + actualPowHSMSignerMessage.getReceiptMerkleProof()); + + assertEquals(Hex.toHexString(pegoutCreationRskTxReceipt.getEncoded()), + actualPowHSMSignerMessage.getTransactionReceipt()); + // Building actualPowHSMSignerMessage twice returns same actualPowHSMSignerMessage + assertEquals(actualPowHSMSignerMessage, actualPowHSMSignerMessage2); } @Test void buildMessageForIndex_fails() { - Transaction rskTx = mock(Transaction.class); - when(rskTx.getHash()).thenReturn(Keccak256.ZERO_HASH); BlockHeaderBuilder blockHeaderBuilder = new BlockHeaderBuilder( mock(ActivationConfig.class)); - Block block = new Block(blockHeaderBuilder.setNumber(1).build(), - Collections.singletonList(rskTx), Collections.emptyList(), true, true); - TransactionReceipt transactionReceipt = mock(TransactionReceipt.class); - when(transactionReceipt.getTransaction()).thenReturn(rskTx); - - when(receiptStore.get(any(byte[].class), any(byte[].class))).thenReturn(Optional.empty()); + Block block = new Block(blockHeaderBuilder.setNumber(1).build(), Collections.singletonList(pegoutCreationRskTx), Collections.emptyList(), true, true); ReleaseCreationInformation releaseCreationInformation = new ReleaseCreationInformation( - block, transactionReceipt, rskTx.getHash(), mock(BtcTransaction.class), - rskTx.getHash()); - PowHSMSignerMessageBuilder sigMessVersion2 = new PowHSMSignerMessageBuilder(receiptStore, + block, pegoutCreationRskTxReceipt, pegoutCreationRskTx.getHash(), mock(BtcTransaction.class), + pegoutConfirmationRskTx.getHash()); + + PowHSMSignerMessageBuilder signerMessageBuilder = new PowHSMSignerMessageBuilder(receiptStore, releaseCreationInformation); + assertThrows(SignerMessageBuilderException.class, - () -> sigMessVersion2.buildMessageForIndex(0)); + () -> signerMessageBuilder.buildMessageForIndex(0)); } @Test @@ -315,20 +277,20 @@ void buildMessageForIndex_whenSegwitMigrationPegoutHasTransactionCreatedEvent_ok byte[] serializedOutpointValues, List expectedOutpointValues) throws SignerMessageBuilderException { // arrange - BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, + BtcTransaction segwitPegoutBtcTx = createPegout(expectedOutpointValues, Collections.singletonList(newFederation.getAddress()), true); List logs = new ArrayList<>(); - addCommonPegoutLogs(logs, pegoutBtcTx); + addCommonPegoutLogs(logs, segwitPegoutBtcTx); LogInfo pegoutTransactionCreatedLog = createPegoutTransactionCreatedLog( - pegoutBtcTx.getHash(), serializedOutpointValues); + segwitPegoutBtcTx.getHash(), serializedOutpointValues); logs.add(pegoutTransactionCreatedLog); pegoutCreationRskTxReceipt.setLogInfoList(logs); - buildMessageForIndexAndExecuteAssertions(expectedOutpointValues, pegoutBtcTx); + buildMessageForIndexAndExecuteAssertions(expectedOutpointValues, segwitPegoutBtcTx); } @ParameterizedTest @@ -337,27 +299,38 @@ void buildMessageForIndex_whenRejectedPeginHasTransactionCreatedEvent_ok( byte[] serializedOutpointValues, List expectedOutpointValues) throws SignerMessageBuilderException { // arrange - BtcTransaction pegoutBtcTx = createPegout(expectedOutpointValues, + BtcTransaction segwitPegoutBtcTx = createPegout(expectedOutpointValues, Collections.singletonList(userAddress), true); List logs = new ArrayList<>(); - LogInfo rejectedPeginLog = creatRejectedPeginLog(pegoutBtcTx.getHash(), + LogInfo rejectedPeginLog = creatRejectedPeginLog(segwitPegoutBtcTx.getHash(), RejectedPeginReason.LEGACY_PEGIN_MULTISIG_SENDER); logs.add(rejectedPeginLog); Coin pegoutAmount = mock(Coin.class); LogInfo releaseRequestedLog = createReleaseRequestedLog(pegoutCreationRskTx.getHash(), - pegoutBtcTx.getHash(), pegoutAmount); + segwitPegoutBtcTx.getHash(), pegoutAmount); logs.add(releaseRequestedLog); LogInfo pegoutTransactionCreatedLog = createPegoutTransactionCreatedLog( - pegoutBtcTx.getHash(), serializedOutpointValues); + segwitPegoutBtcTx.getHash(), serializedOutpointValues); logs.add(pegoutTransactionCreatedLog); pegoutCreationRskTxReceipt.setLogInfoList(logs); - buildMessageForIndexAndExecuteAssertions(expectedOutpointValues, pegoutBtcTx); + buildMessageForIndexAndExecuteAssertions(expectedOutpointValues, segwitPegoutBtcTx); + } + + private static List serializedAndDeserializedOutpointValuesArgProvider() { + List arguments = new ArrayList<>(); + // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 + arguments.add(Arguments.of(Hex.decode("FE80F0FA02"), coinListOf(50_000_000))); + arguments.add(Arguments.of(Hex.decode("FEC0687804"), coinListOf(75_000_000))); + arguments.add(Arguments.of(Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), + coinListOf(50_000_000, 75_000_000, 100_000_000))); + + return arguments; } private void buildMessageForIndexAndExecuteAssertions(List expectedOutpointValues, From 8c639a56dd2c8060c673287d81b137305df2e183 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Mon, 8 Jul 2024 17:34:44 -0400 Subject: [PATCH 5/9] Refactor ReleaseCreationInformationGetterTest and PowHSMSigningClientBtcTestclass. Move test setup to beforeEach setUp method --- .../bitcoin/BtcTransactionBuilder.java | 81 +++-- .../client/PowHSMSigningClientBtcTest.java | 59 ++-- .../PowHSMSignerMessageBuilderTest.java | 43 +-- .../hsm/message/PowHSMSignerMessageTest.java | 41 +-- .../ReleaseCreationInformationGetterTest.java | 306 ++++++------------ 5 files changed, 214 insertions(+), 316 deletions(-) diff --git a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java index 240d88f3..7e1fd4e1 100644 --- a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java +++ b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java @@ -18,54 +18,33 @@ public final class BtcTransactionBuilder { private final NetworkParameters networkParameters; private final Map transactionWitnesses = new HashMap<>(); - private final List transactionInputs = new ArrayList<>(); - private final List transactionOutputs = new ArrayList<>(); + private final List inputs = new ArrayList<>(); + private final List outputs = new ArrayList<>(); public BtcTransactionBuilder(NetworkParameters networkParameters) { this.networkParameters = networkParameters; } - public BtcTransactionBuilder addInput(Coin utxoAmount) { - addInput(utxoAmount, null, null); + public BtcTransactionBuilder withInput(TransactionInput transactionInput) { + inputs.add(transactionInput); return this; } - public BtcTransactionBuilder addInputWithScriptSig(Coin utxoAmount, Script scriptSig) { - addInput(utxoAmount, scriptSig, null); - return this; - } - - public BtcTransactionBuilder addInputWithWitness(Coin utxoAmount, - TransactionWitness transactionWitness) { - addInput(utxoAmount, null, transactionWitness); + public BtcTransactionBuilder withInput(TransactionOutput transactionOutput) { + TransactionInput transactionInput = new TransactionInput(networkParameters, null, + new byte[]{}, transactionOutput.getOutPointFor(), transactionOutput.getValue()); + inputs.add(transactionInput); return this; } - private void addInput(Coin utxoAmount, Script scriptSig, + public BtcTransactionBuilder withWitness(int inputIndex, TransactionWitness transactionWitness) { - int idx = transactionInputs.size(); - TransactionOutPoint transactionOutpoint = new TransactionOutPoint(networkParameters, idx, - BitcoinTestUtils.createHash(idx)); - TransactionInput transactionInput = new TransactionInput( - networkParameters, null, new byte[]{}, transactionOutpoint, utxoAmount - ); - if (scriptSig != null) { - transactionInput.setScriptSig(scriptSig); - } else if (transactionWitness != null) { - transactionWitnesses.put(idx, transactionWitness); - } - transactionInputs.add(transactionInput); - } - - public BtcTransactionBuilder addInputFrom(TransactionOutput transactionOutput) { - TransactionInput transactionInput = new TransactionInput(networkParameters, null, - new byte[]{}, transactionOutput.getOutPointFor(), transactionOutput.getValue()); - transactionInputs.add(transactionInput); + transactionWitnesses.put(inputIndex, transactionWitness); return this; } - public BtcTransactionBuilder addOutput(Coin amount, Address address) { - transactionOutputs.add( + public BtcTransactionBuilder withOutput(Coin amount, Address address) { + outputs.add( new TransactionOutput(networkParameters, null, amount, address) ); return this; @@ -79,7 +58,7 @@ public BtcTransaction build() { } private void addInputs(BtcTransaction btcTransaction) { - transactionInputs.forEach(transactionInput -> { + inputs.forEach(transactionInput -> { int idx = btcTransaction.getInputs().size(); btcTransaction.addInput(transactionInput); if (transactionWitnesses.containsKey(idx)) { @@ -89,6 +68,38 @@ private void addInputs(BtcTransaction btcTransaction) { } private void addOutputs(BtcTransaction btcTransaction) { - transactionOutputs.forEach(btcTransaction::addOutput); + outputs.forEach(btcTransaction::addOutput); + } + + public class InputBuilder { + + private final Coin amount; + private Script scriptSig; + private int outpointIndex = 0; + + public InputBuilder(Coin amount) { + this.amount = amount; + } + + public InputBuilder withScriptSig(Script scriptSig) { + this.scriptSig = scriptSig; + return this; + } + + public InputBuilder withOutpointIndex(int outpointIndex) { + this.outpointIndex = outpointIndex; + return this; + } + + public TransactionInput build() { + TransactionOutPoint transactionOutpoint = new TransactionOutPoint(networkParameters, + outpointIndex, + BitcoinTestUtils.createHash(outpointIndex)); + TransactionInput transactionInput = new TransactionInput( + networkParameters, null, new byte[]{}, transactionOutpoint, amount + ); + transactionInput.setScriptSig(scriptSig); + return transactionInput; + } } } diff --git a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java index c1026f45..211a125d 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java @@ -265,7 +265,7 @@ void signNoS() throws Exception { @ParameterizedTest @MethodSource("legacyPegoutArgProvider") - void sign_whenBatchPegoutHasNotPegoutTransactionCreatedEvent_returnsOk(int hsmVersion, + void sign_whenBatchPegoutHasNotPegoutTransactionCreatedEvent_returnsOk(HSMVersion hsmVersion, List expectedOutpointValues) throws JsonRpcException, SignerMessageBuilderException, HSMClientException { // arrange @@ -288,7 +288,7 @@ void sign_whenBatchPegoutHasNotPegoutTransactionCreatedEvent_returnsOk(int hsmVe pegoutCreationBlock, pegoutCreationRskTxReceipt, pegoutCreationRskTx.getHash(), pegoutBtcTx, pegoutConfirmationRskTx.getHash()); - client = new PowHSMSigningClientBtc(hsmClientProtocol, hsmVersion); + client = new PowHSMSigningClientBtc(hsmClientProtocol, hsmVersion.getNumber()); PowHSMSignerMessageBuilder powHSMSignerMessageBuilder = new PowHSMSignerMessageBuilder( receiptStore, releaseCreationInformation); @@ -298,7 +298,7 @@ void sign_whenBatchPegoutHasNotPegoutTransactionCreatedEvent_returnsOk(int hsmVe @ParameterizedTest @MethodSource("signArgProvider") - void sign_whenSegwitBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk(int hsmVersion, + void sign_whenSegwitBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk(HSMVersion hsmVersion, byte[] serializedOutpointValues, List expectedOutpointValues) throws JsonRpcException, SignerMessageBuilderException, HSMClientException { // arrange @@ -325,7 +325,7 @@ void sign_whenSegwitBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk(int hs pegoutCreationBlock, pegoutCreationRskTxReceipt, pegoutCreationRskTx.getHash(), pegoutBtcTx, pegoutConfirmationRskTx.getHash()); - client = new PowHSMSigningClientBtc(hsmClientProtocol, hsmVersion); + client = new PowHSMSigningClientBtc(hsmClientProtocol, hsmVersion.getNumber()); PowHSMSignerMessageBuilder powHSMSignerMessageBuilder = new PowHSMSignerMessageBuilder( receiptStore, releaseCreationInformation); @@ -335,7 +335,7 @@ void sign_whenSegwitBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk(int hs @ParameterizedTest @MethodSource("signArgProvider") - void sign_whenSegwitMigrationPegoutHasPegoutTransactionCreatedEvent_returnsOk(int hsmVersion, + void sign_whenSegwitMigrationPegoutHasPegoutTransactionCreatedEvent_returnsOk(HSMVersion hsmVersion, byte[] serializedOutpointValues, List expectedOutpointValues) throws JsonRpcException, SignerMessageBuilderException, HSMClientException { // arrange @@ -357,7 +357,7 @@ void sign_whenSegwitMigrationPegoutHasPegoutTransactionCreatedEvent_returnsOk(in pegoutCreationBlock, pegoutCreationRskTxReceipt, pegoutCreationRskTx.getHash(), pegoutBtcTx, pegoutConfirmationRskTx.getHash()); - client = new PowHSMSigningClientBtc(hsmClientProtocol, hsmVersion); + client = new PowHSMSigningClientBtc(hsmClientProtocol, hsmVersion.getNumber()); PowHSMSignerMessageBuilder powHSMSignerMessageBuilder = new PowHSMSignerMessageBuilder( receiptStore, releaseCreationInformation); @@ -431,29 +431,32 @@ private BtcTransaction createPegout(List outpointValues, oldFederation); Coin fee = Coin.MILLICOIN; - for (int idx = 0; idx < outpointValues.size(); idx++) { - Coin outpointValue = outpointValues.get(idx); + for (int inputIndex = 0; inputIndex < outpointValues.size(); inputIndex++) { + Coin outpointValue = outpointValues.get(inputIndex); Coin amountToSend = outpointValue.minus(fee); + // Iterate over the addresses using inputIndex % addresses.size() to have outputs to different addresses Address destinationAddress = destinationAddresses.get( - idx % destinationAddresses.size()); - - btcTransactionBuilder.addInputWithScriptSig(outpointValue, - inputScriptThatSpendsFromTheFederation); - // Iterate over the addresses using idx % addresses.size() to have outputs to different addresses - btcTransactionBuilder.addOutput(amountToSend, destinationAddress); + inputIndex % destinationAddresses.size()); + + btcTransactionBuilder + .withInput( + btcTransactionBuilder.new InputBuilder(outpointValue).withOutpointIndex( + inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation).build() + ) + .withOutput(amountToSend, destinationAddress); + + // TODO: change this dummy witness for a real witness once segwit is fully implemented in bitcoinj-thin + // make it a segwit tx by adding a single witness + if (segwit) { + TransactionWitness witness = new TransactionWitness(1); + witness.setPush(0, new byte[]{1}); + + btcTransactionBuilder.withWitness(inputIndex, witness); + } } - BtcTransaction btcTransaction = btcTransactionBuilder.build(); - - // make the tx segwit by adding a single witness - if (segwit) { - TransactionWitness witness = new TransactionWitness(1); - witness.setPush(0, new byte[]{1}); - - int fistInputIdx = 0; - btcTransaction.setWitness(fistInputIdx, witness); - } - return btcTransaction; + return btcTransactionBuilder.build(); } private List
createDestinationAddresses(int numberOfAddresses) { @@ -463,12 +466,12 @@ private List
createDestinationAddresses(int numberOfAddresses) { .collect(Collectors.toList()); } - private void signAndExecuteAssertions(int hsmVersion, List expectedOutpointValues, + private void signAndExecuteAssertions(HSMVersion hsmVersion, List expectedOutpointValues, PowHSMSignerMessageBuilder powHSMSignerMessageBuilder) throws JsonRpcException, SignerMessageBuilderException, HSMClientException { final int numOfInputsToSign = expectedOutpointValues.size(); - ObjectNode expectedPublicKeyRequest = buildGetPublicKeyCommand(hsmVersion); + ObjectNode expectedPublicKeyRequest = buildGetPublicKeyCommand(hsmVersion.getNumber()); ObjectNode publicKeyResponse = buildResponse(0); publicKeyResponse.put(PUB_KEY.getFieldName(), Hex.toHexString(signerPk.getPubKey())); when(jsonRpcClientMock.send(expectedPublicKeyRequest)).thenReturn(publicKeyResponse); @@ -479,7 +482,7 @@ private void signAndExecuteAssertions(int hsmVersion, List expectedOutpoin PowHSMSignerMessage powHSMSignerMessage = (PowHSMSignerMessage) powHSMSignerMessageBuilder.buildMessageForIndex( inputIndex); - ObjectNode expectedSignCommand = buildSignCommand(hsmVersion, + ObjectNode expectedSignCommand = buildSignCommand(hsmVersion.getNumber(), powHSMSignerMessage); when(jsonRpcClientMock.send(expectedSignCommand)).thenReturn(response); diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java index 517db2a3..b229c3c9 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java @@ -383,35 +383,38 @@ private BtcTransaction createPegout(List outpointValues, BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); - // TODO: improve this method to create a more realistic btc segwit transaction + // TODO: improve this test to create a more realistic btc segwit transaction // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit Script inputScriptThatSpendsFromTheFederation = createBaseInputScriptThatSpendsFromTheFederation( oldFederation); Coin fee = Coin.MILLICOIN; - for (int idx = 0; idx < outpointValues.size(); idx++) { - Coin outpointValue = outpointValues.get(idx); + for (int inputIndex = 0; inputIndex < outpointValues.size(); inputIndex++) { + Coin outpointValue = outpointValues.get(inputIndex); Coin amountToSend = outpointValue.minus(fee); + // Iterate over the addresses using inputIndex % addresses.size() to have outputs to different addresses Address destinationAddress = destinationAddresses.get( - idx % destinationAddresses.size()); - - btcTransactionBuilder.addInputWithScriptSig(outpointValue, - inputScriptThatSpendsFromTheFederation); - // Iterate over the addresses using idx % addresses.size() to have outputs to different addresses - btcTransactionBuilder.addOutput(amountToSend, destinationAddress); + inputIndex % destinationAddresses.size()); + + btcTransactionBuilder + .withInput( + btcTransactionBuilder.new InputBuilder(outpointValue).withOutpointIndex( + inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation).build() + ) + .withOutput(amountToSend, destinationAddress); + + if (segwit) { + // TODO: change this dummy witness for a real witness once segwit is fully implemented in bitcoinj-thin + // make it a segwit tx by adding a single witness + TransactionWitness witness = new TransactionWitness(1); + witness.setPush(0, new byte[]{1}); + + btcTransactionBuilder.withWitness(inputIndex, witness); + } } - BtcTransaction btcTransaction = btcTransactionBuilder.build(); - - // make the tx segwit by adding a single witness - if (segwit) { - TransactionWitness witness = new TransactionWitness(1); - witness.setPush(0, new byte[]{1}); - - int fistInputIdx = 0; - btcTransaction.setWitness(fistInputIdx, witness); - } - return btcTransaction; + return btcTransactionBuilder.build(); } private void addCommonPegoutLogs(List logs, BtcTransaction pegoutBtcTx) { diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java index f61d85a1..d2d1ec43 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java @@ -233,9 +233,9 @@ void getMessageToSign_whenSighashSegwitMode_ok() { BtcTransaction segwitPegoutBtcTx = createSegwitPegout(outpointValues); - TransactionReceipt txReceipt = new TransactionReceipt(); + txReceipt = new TransactionReceipt(); - List receiptMerkleProof = new ArrayList<>(); + receiptMerkleProof = new ArrayList<>(); receiptMerkleProof.add(new Trie()); Sha256Hash sigHash = BitcoinTestUtils.createHash(1); @@ -270,27 +270,30 @@ private BtcTransaction createSegwitPegout(List outpointValues) { "userAddress"); List
destinationAddresses = Arrays.asList(userAddress, userAddress2); Coin fee = Coin.MILLICOIN; - for (int idx = 0; idx < outpointValues.size(); idx++) { - Coin outpointValue = outpointValues.get(idx); + for (int inputIndex = 0; inputIndex < outpointValues.size(); inputIndex++) { + Coin outpointValue = outpointValues.get(inputIndex); Coin amountToSend = outpointValue.minus(fee); + // Iterate over the addresses using inputIndex % addresses.size() to have outputs to different addresses Address destinationAddress = destinationAddresses.get( - idx % destinationAddresses.size()); - - btcTransactionBuilder.addInputWithScriptSig(outpointValue, - inputScriptThatSpendsFromTheFederation); - // Iterate over the addresses using idx % addresses.size() to have outputs to different addresses - btcTransactionBuilder.addOutput(amountToSend, destinationAddress); + inputIndex % destinationAddresses.size()); + + btcTransactionBuilder + .withInput( + btcTransactionBuilder.new InputBuilder(outpointValue).withOutpointIndex( + inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation).build() + ) + .withOutput(amountToSend, destinationAddress); + + // TODO: change this dummy witness for a real witness once segwit is fully implemented in bitcoinj-thin + // make it a segwit tx by adding a single witness + TransactionWitness witness = new TransactionWitness(1); + witness.setPush(0, new byte[]{1}); + + btcTransactionBuilder.withWitness(inputIndex, witness); } - BtcTransaction btcTransaction = btcTransactionBuilder.build(); - - // make the tx segwit by adding a single witness - TransactionWitness witness = new TransactionWitness(1); - witness.setPush(0, new byte[]{1}); - - int fistInputIdx = 0; - btcTransaction.setWitness(fistInputIdx, witness); - return btcTransaction; + return btcTransactionBuilder.build(); } private void assertHsmMessageValues(BtcTransaction segwitPegoutBtcTx, diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformationGetterTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformationGetterTest.java index 7a853e68..876bd945 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformationGetterTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformationGetterTest.java @@ -46,6 +46,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; class ReleaseCreationInformationGetterTest { @@ -54,25 +55,8 @@ class ReleaseCreationInformationGetterTest { private Block pegoutCreationBlock; private TransactionReceipt pegoutCreationRskTxReceipt; private TransactionInfo pegoutCreationRskTxInfo; - - private static List getTxInfoToSignArgProvider() { - List arguments = new ArrayList<>(); - - for (HSMVersion hsmVersion : HSMVersion.values()) { - arguments.add( - Arguments.of(hsmVersion.getNumber(), Hex.decode("00"), Collections.singletonList(Coin.ZERO))); - arguments.add( - Arguments.of(hsmVersion.getNumber(), Hex.decode("01"), - Collections.singletonList(Coin.SATOSHI))); - // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 - arguments.add(Arguments.of(hsmVersion.getNumber(), Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), - coinListOf(50_000_000, 75_000_000, 100_000_000))); - arguments.add(Arguments.of(hsmVersion.getNumber(), Hex.decode("FFFFFFFFFFFFFFFF7F"), - coinListOf(Long.MAX_VALUE))); - } - - return arguments; - } + private ReceiptStore receiptStore; + private BlockStore blockStore; @BeforeEach void setUp() { @@ -94,66 +78,48 @@ void setUp() { when(pegoutCreationRskTxInfo.getReceipt()).thenReturn(pegoutCreationRskTxReceipt); when(pegoutCreationRskTxInfo.getBlockHash()).thenReturn( pegoutCreationBlock.getHash().getBytes()); - } + blockStore = mock(BlockStore.class); + when(blockStore.getBlockByHash(pegoutCreationBlock.getHash().getBytes())).thenReturn( + pegoutCreationBlock); + when(blockStore.getChainBlockByNumber(pegoutCreationBlock.getNumber())).thenReturn( + pegoutCreationBlock); - @Test - void createGetTxInfoToSign_returnOK() throws HSMReleaseCreationInformationException { - Keccak256 blockHash = TestUtils.createHash(3); - Keccak256 rskTxHash = TestUtils.createHash(1); - byte[] btcTxHash = TestUtils.createHash(2).getBytes(); - BtcTransaction pegoutBtcTransaction = mock(BtcTransaction.class); - when(pegoutBtcTransaction.getHash()).thenReturn(Sha256Hash.wrap(btcTxHash)); + receiptStore = mock(ReceiptStore.class); + when(receiptStore.get(pegoutCreationRskTx.getHash().getBytes(), + pegoutCreationBlock.getHash().getBytes())).thenReturn( + Optional.of(pegoutCreationRskTxInfo)); - CallTransaction.Function releaseRequestedEvent = BridgeEvents.RELEASE_REQUESTED.getEvent(); - byte[] releaseRequestedSignatureTopic = releaseRequestedEvent.encodeSignatureLong(); - List topics = new ArrayList<>(); - topics.add(DataWord.valueOf(releaseRequestedSignatureTopic)); - topics.add(DataWord.valueOf(rskTxHash.getBytes())); - topics.add(DataWord.valueOf(btcTxHash)); + when(receiptStore.getInMainChain(pegoutCreationRskTx.getHash().getBytes(), + blockStore)).thenReturn(Optional.of( + pegoutCreationRskTxInfo)); + } + @ParameterizedTest + @ValueSource(ints = {2, 3, 4}) + void createGetTxInfoToSign_returnOK(int versionNumber) + throws HSMReleaseCreationInformationException { List logs = new ArrayList<>(); - logs.add(new LogInfo(PrecompiledContracts.BRIDGE_ADDR.getBytes(), topics, null)); - - Transaction transaction = mock(Transaction.class); - when(transaction.getHash()).thenReturn(rskTxHash); - when(transaction.getReceiveAddress()).thenReturn(PrecompiledContracts.BRIDGE_ADDR); - TransactionReceipt transactionReceipt = new TransactionReceipt(); - transactionReceipt.setLogInfoList(logs); - - TransactionInfo transactionInfo = mock(TransactionInfo.class); - when(transactionInfo.getReceipt()).thenReturn(transactionReceipt); - when(transactionInfo.getBlockHash()).thenReturn(blockHash.getBytes()); - - Block block = mock(Block.class); - when(block.getNumber()).thenReturn(666L); - when(block.getHash()).thenReturn(blockHash); - when(block.getTransactionsList()).thenReturn(Collections.singletonList(transaction)); + ECKey senderKey = new ECKey(); + RskAddress senderAddress = new RskAddress(senderKey.getAddress()); + LogInfo updateCollectionsLog = createUpdateCollectionsLog(senderAddress); + logs.add(updateCollectionsLog); - BlockStore blockStore = mock(BlockStore.class); - when(blockStore.getBlockByHash(blockHash.getBytes())).thenReturn(block); - when(blockStore.getChainBlockByNumber(666L)).thenReturn(block); + Coin pegoutAmount = mock(Coin.class); + LogInfo releaseRequestedLog = createReleaseRequestedLog(pegoutCreationRskTx.getHash(), + pegoutBtcTx.getHash(), pegoutAmount); + logs.add(releaseRequestedLog); - ReceiptStore receiptStore = mock(ReceiptStore.class); - when(receiptStore.getInMainChain(rskTxHash.getBytes(), blockStore)).thenReturn( - Optional.of(transactionInfo)); + pegoutCreationRskTxReceipt.setLogInfoList(logs); ReleaseCreationInformationGetter pegoutCreationInformation = new ReleaseCreationInformationGetter( receiptStore, blockStore ); - // HSM V2 - createGetTxInfoToSign_returnOK(pegoutCreationInformation, rskTxHash, pegoutBtcTransaction, - block, transactionReceipt, HSMVersion.V2.getNumber()); - - // HSM V3 - createGetTxInfoToSign_returnOK(pegoutCreationInformation, rskTxHash, pegoutBtcTransaction, - block, transactionReceipt, HSMVersion.V3.getNumber()); - // HSM V4 - createGetTxInfoToSign_returnOK(pegoutCreationInformation, rskTxHash, pegoutBtcTransaction, - block, transactionReceipt, HSMVersion.V4.getNumber()); + createGetTxInfoToSign_returnOK(pegoutCreationInformation, pegoutCreationRskTx.getHash(), + pegoutBtcTx, pegoutCreationBlock, pegoutCreationRskTxReceipt, versionNumber); } private void createGetTxInfoToSign_returnOK( @@ -233,11 +199,11 @@ void createGetTxInfoToSign_returnOK_SecondBlock() when(secondBlock.getTransactionsList()).thenReturn( Collections.singletonList(transactionInSecondBlock)); - BlockStore blockStore = mock(BlockStore.class); + blockStore = mock(BlockStore.class); when(blockStore.getBlockByHash(blockHash.getBytes())).thenReturn(block); when(blockStore.getChainBlockByNumber(anyLong())).thenReturn(secondBlock); - ReceiptStore receiptStore = mock(ReceiptStore.class); + receiptStore = mock(ReceiptStore.class); when(receiptStore.getInMainChain(rskTxHash.getBytes(), blockStore)).thenReturn( Optional.of(transactionInfo)); when(receiptStore.getInMainChain(rskTxHashInSecondBlock.getBytes(), blockStore)).thenReturn( @@ -255,34 +221,18 @@ void createGetTxInfoToSign_returnOK_SecondBlock() releaseCreationInformation.getTransactionReceipt()); assertEquals(rskTxHash, releaseCreationInformation.getPegoutCreationRskTxHash()); assertEquals(pegoutBtcTransaction, releaseCreationInformation.getPegoutBtcTx()); - } @Test void createGetTxInfoToSign_transactionHashNotFoundInBlock() { Keccak256 blockHash = TestUtils.createHash(3); - Keccak256 rskTxHash = TestUtils.createHash(1); - byte[] btcTxHash = TestUtils.createHash(2).getBytes(); - BtcTransaction pegoutBtcTransaction = mock(BtcTransaction.class); - when(pegoutBtcTransaction.getHash()).thenReturn(Sha256Hash.wrap(btcTxHash)); - - Transaction transaction = mock(Transaction.class); - when(transaction.getHash()).thenReturn(rskTxHash); - - TransactionInfo transactionInfo = mock(TransactionInfo.class); - when(transactionInfo.getBlockHash()).thenReturn(blockHash.getBytes()); - Block block = mock(Block.class); when(block.getHash()).thenReturn(blockHash); when(block.getTransactionsList()).thenReturn(new ArrayList<>()); - BlockStore blockStore = mock(BlockStore.class); + blockStore = mock(BlockStore.class); when(blockStore.getBlockByHash(blockHash.getBytes())).thenReturn(block); - ReceiptStore receiptStore = mock(ReceiptStore.class); - when(receiptStore.getInMainChain(rskTxHash.getBytes(), blockStore)).thenReturn( - Optional.of(transactionInfo)); - ReleaseCreationInformationGetter pegoutCreationInformation = new ReleaseCreationInformationGetter( receiptStore, blockStore @@ -291,41 +241,13 @@ void createGetTxInfoToSign_transactionHashNotFoundInBlock() { assertThrows(HSMReleaseCreationInformationException.class, () -> pegoutCreationInformation.getTxInfoToSign( HSMVersion.V2.getNumber(), - rskTxHash, - pegoutBtcTransaction + pegoutCreationRskTx.getHash(), + pegoutBtcTx )); } @Test void createGetTxInfoToSignV2_noEventFound_noBlockFound() { - Keccak256 blockHash = TestUtils.createHash(3); - Keccak256 rskTxHash = TestUtils.createHash(1); - byte[] btcTxHash = TestUtils.createHash(2).getBytes(); - BtcTransaction pegoutBtcTransaction = mock(BtcTransaction.class); - when(pegoutBtcTransaction.getHash()).thenReturn(Sha256Hash.wrap(btcTxHash)); - - Transaction transaction = mock(Transaction.class); - when(transaction.getHash()).thenReturn(rskTxHash); - when(transaction.getReceiveAddress()).thenReturn(PrecompiledContracts.BRIDGE_ADDR); - - TransactionReceipt transactionReceipt = new TransactionReceipt(); - transactionReceipt.setLogInfoList(new ArrayList<>()); - - TransactionInfo transactionInfo = mock(TransactionInfo.class); - when(transactionInfo.getReceipt()).thenReturn(transactionReceipt); - when(transactionInfo.getBlockHash()).thenReturn(blockHash.getBytes()); - - Block block = mock(Block.class); - when(block.getHash()).thenReturn(blockHash); - when(block.getTransactionsList()).thenReturn(Collections.singletonList(transaction)); - - BlockStore blockStore = mock(BlockStore.class); - when(blockStore.getBlockByHash(blockHash.getBytes())).thenReturn(block); - - ReceiptStore receiptStore = mock(ReceiptStore.class); - when(receiptStore.getInMainChain(rskTxHash.getBytes(), blockStore)).thenReturn( - Optional.of(transactionInfo)); - ReleaseCreationInformationGetter pegoutCreationInformation = new ReleaseCreationInformationGetter( receiptStore, blockStore @@ -334,43 +256,29 @@ void createGetTxInfoToSignV2_noEventFound_noBlockFound() { assertThrows(HSMReleaseCreationInformationException.class, () -> pegoutCreationInformation.getTxInfoToSign( 2, - rskTxHash, - pegoutBtcTransaction + pegoutCreationRskTx.getHash(), + pegoutBtcTx )); } @Test void createGetTxInfoToSignV2_noEventFound_BestBlockFound() { Keccak256 blockHash = TestUtils.createHash(3); - Keccak256 rskTxHash = TestUtils.createHash(1); - byte[] btcTxHash = TestUtils.createHash(2).getBytes(); - BtcTransaction pegoutBtcTransaction = mock(BtcTransaction.class); - when(pegoutBtcTransaction.getHash()).thenReturn(Sha256Hash.wrap(btcTxHash)); - - Transaction transaction = mock(Transaction.class); - when(transaction.getHash()).thenReturn(rskTxHash); - when(transaction.getReceiveAddress()).thenReturn(PrecompiledContracts.BRIDGE_ADDR); - - TransactionReceipt transactionReceipt = new TransactionReceipt(); - transactionReceipt.setLogInfoList(new ArrayList<>()); - - TransactionInfo transactionInfo = mock(TransactionInfo.class); - when(transactionInfo.getReceipt()).thenReturn(transactionReceipt); - when(transactionInfo.getBlockHash()).thenReturn(blockHash.getBytes()); - long blockNumber = 4L; Block block = mock(Block.class); when(block.getHash()).thenReturn(blockHash); when(block.getNumber()).thenReturn(blockNumber); - BlockStore blockStore = mock(BlockStore.class); + blockStore = mock(BlockStore.class); when(blockStore.getBlockByHash(blockHash.getBytes())).thenReturn(block); when(blockStore.getChainBlockByNumber(blockNumber)).thenReturn(block); when(blockStore.getBestBlock()).thenReturn(block); - ReceiptStore receiptStore = mock(ReceiptStore.class); + Keccak256 rskTxHash = pegoutCreationRskTx.getHash(); + + receiptStore = mock(ReceiptStore.class); when(receiptStore.getInMainChain(rskTxHash.getBytes(), blockStore)).thenReturn( - Optional.of(transactionInfo)); + Optional.of(pegoutCreationRskTxInfo)); ReleaseCreationInformationGetter pegoutCreationInformation = new ReleaseCreationInformationGetter( receiptStore, @@ -381,7 +289,7 @@ void createGetTxInfoToSignV2_noEventFound_BestBlockFound() { () -> pegoutCreationInformation.getTxInfoToSign( 2, rskTxHash, - pegoutBtcTransaction + pegoutBtcTx )); } @@ -389,21 +297,10 @@ void createGetTxInfoToSignV2_noEventFound_BestBlockFound() { void getTxInfoToSign_whenTransactionReceiptNotFoundInSubsequentBlock_shouldThrowHSMReleaseCreationInformationException() { // The event that is searched is not found in the first block Keccak256 blockHash = TestUtils.createHash(3); - Keccak256 rskTxHash = TestUtils.createHash(1); - - Transaction transaction = mock(Transaction.class); - when(transaction.getHash()).thenReturn(rskTxHash); - - TransactionReceipt transactionReceipt = mock(TransactionReceipt.class); - when(transactionReceipt.getTransaction()).thenReturn(transaction); - - TransactionInfo transactionInfo = mock(TransactionInfo.class); - when(transactionInfo.getReceipt()).thenReturn(transactionReceipt); - when(transactionInfo.getBlockHash()).thenReturn(blockHash.getBytes()); Block block = mock(Block.class); when(block.getHash()).thenReturn(blockHash); - when(block.getTransactionsList()).thenReturn(Collections.singletonList(transaction)); + when(block.getTransactionsList()).thenReturn(Collections.singletonList(pegoutCreationRskTx)); // The event is now searched in the following block Keccak256 secondBlockHash = TestUtils.createHash(5); @@ -417,13 +314,15 @@ void getTxInfoToSign_whenTransactionReceiptNotFoundInSubsequentBlock_shouldThrow when(secondBlock.getTransactionsList()).thenReturn( Collections.singletonList(transactionInSecondBlock)); - BlockStore blockStore = mock(BlockStore.class); + blockStore = mock(BlockStore.class); when(blockStore.getBlockByHash(blockHash.getBytes())).thenReturn(block); when(blockStore.getChainBlockByNumber(anyLong())).thenReturn(secondBlock); - ReceiptStore receiptStore = mock(ReceiptStore.class); + Keccak256 rskTxHash = pegoutCreationRskTx.getHash(); + + receiptStore = mock(ReceiptStore.class); when(receiptStore.getInMainChain(rskTxHash.getBytes(), blockStore)).thenReturn( - Optional.of(transactionInfo)); + Optional.of(pegoutCreationRskTxInfo)); when(receiptStore.getInMainChain(rskTxHashInSecondBlock.getBytes(), blockStore)).thenReturn( Optional.empty()); @@ -454,15 +353,7 @@ void getTxInfoToSign_whenBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk( // Arrange List logs = new ArrayList<>(); - ECKey senderKey = new ECKey(); - RskAddress senderAddress = new RskAddress(senderKey.getAddress()); - LogInfo updateCollectionsLog = createUpdateCollectionsLog(senderAddress); - logs.add(updateCollectionsLog); - - Coin pegoutAmount = mock(Coin.class); - LogInfo releaseRequestedLog = createReleaseRequestedLog(pegoutCreationRskTx.getHash(), - pegoutBtcTx.getHash(), pegoutAmount); - logs.add(releaseRequestedLog); + addCommonPegoutLogs(logs, pegoutBtcTx, serializedOutpointValues); List pegoutRequestRskTxHashes = Collections.singletonList( TestUtils.createHash(10)); @@ -470,23 +361,13 @@ void getTxInfoToSign_whenBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk( pegoutRequestRskTxHashes); logs.add(batchPegoutCreatedLog); - LogInfo pegoutTransactionCreatedLog = createPegoutTransactionCreatedLog( - pegoutBtcTx.getHash(), serializedOutpointValues); - logs.add(pegoutTransactionCreatedLog); + ECKey senderKey = new ECKey(); + RskAddress senderAddress = new RskAddress(senderKey.getAddress()); + LogInfo updateCollectionsLog = createUpdateCollectionsLog(senderAddress); + logs.add(updateCollectionsLog); pegoutCreationRskTxReceipt.setLogInfoList(logs); - BlockStore blockStore = mock(BlockStore.class); - when(blockStore.getBlockByHash(pegoutCreationBlock.getHash().getBytes())).thenReturn( - pegoutCreationBlock); - when(blockStore.getChainBlockByNumber(pegoutCreationBlock.getNumber())).thenReturn( - pegoutCreationBlock); - - ReceiptStore receiptStore = mock(ReceiptStore.class); - when(receiptStore.getInMainChain(pegoutCreationRskTx.getHash().getBytes(), - blockStore)).thenReturn(Optional.of( - pegoutCreationRskTxInfo)); - ReleaseCreationInformationGetter releaseCreationInformationGetter = new ReleaseCreationInformationGetter( receiptStore, blockStore @@ -511,7 +392,7 @@ void getTxInfoToSign_whenBatchPegoutHasPegoutTransactionCreatedEvent_returnsOk( @ParameterizedTest @MethodSource("getTxInfoToSignArgProvider") - void getTxInfoToSign_whenPegoutHasNotBatchPegoutButHasPegoutCreatedEvent_returnsOk( + void getTxInfoToSign_whenMigrationHasPegoutCreatedEvent_returnsOk( int version, byte[] serializedOutpointValues, List expectedOutpointValues @@ -528,28 +409,10 @@ void getTxInfoToSign_whenPegoutHasNotBatchPegoutButHasPegoutCreatedEvent_returns LogInfo updateCollectionsLog = createUpdateCollectionsLog(senderAddress); logs.add(updateCollectionsLog); - Coin pegoutAmount = mock(Coin.class); - LogInfo releaseRequestedLog = createReleaseRequestedLog(pegoutCreationRskTx.getHash(), - pegoutBtcTx.getHash(), pegoutAmount); - logs.add(releaseRequestedLog); - - LogInfo pegoutTransactionCreatedLog = createPegoutTransactionCreatedLog( - pegoutBtcTx.getHash(), serializedOutpointValues); - logs.add(pegoutTransactionCreatedLog); + addCommonPegoutLogs(logs, pegoutBtcTx, serializedOutpointValues); pegoutCreationRskTxReceipt.setLogInfoList(logs); - BlockStore blockStore = mock(BlockStore.class); - when(blockStore.getBlockByHash(pegoutCreationBlock.getHash().getBytes())).thenReturn( - pegoutCreationBlock); - when(blockStore.getChainBlockByNumber(pegoutCreationBlock.getNumber())).thenReturn( - pegoutCreationBlock); - - ReceiptStore receiptStore = mock(ReceiptStore.class); - when(receiptStore.getInMainChain(pegoutCreationRskTx.getHash().getBytes(), - blockStore)).thenReturn(Optional.of( - pegoutCreationRskTxInfo)); - ReleaseCreationInformationGetter releaseCreationInformationGetter = new ReleaseCreationInformationGetter( receiptStore, blockStore @@ -586,32 +449,14 @@ void getTxInfoToSign_whenRejectedPeginHasPegoutCreatedEvent_returnsOk( List logs = new ArrayList<>(); + addCommonPegoutLogs(logs, pegoutBtcTx, serializedOutpointValues); + LogInfo rejectedPeginLog = creatRejectedPeginLog(pegoutBtcTx.getHash(), RejectedPeginReason.LEGACY_PEGIN_MULTISIG_SENDER); logs.add(rejectedPeginLog); - Coin pegoutAmount = mock(Coin.class); - LogInfo releaseRequestedLog = createReleaseRequestedLog(pegoutCreationRskTx.getHash(), - pegoutBtcTx.getHash(), pegoutAmount); - logs.add(releaseRequestedLog); - - LogInfo pegoutTransactionCreatedLog = createPegoutTransactionCreatedLog( - pegoutBtcTx.getHash(), serializedOutpointValues); - logs.add(pegoutTransactionCreatedLog); - pegoutCreationRskTxReceipt.setLogInfoList(logs); - BlockStore blockStore = mock(BlockStore.class); - when(blockStore.getBlockByHash(pegoutCreationBlock.getHash().getBytes())).thenReturn( - pegoutCreationBlock); - when(blockStore.getChainBlockByNumber(pegoutCreationBlock.getNumber())).thenReturn( - pegoutCreationBlock); - - ReceiptStore receiptStore = mock(ReceiptStore.class); - when(receiptStore.getInMainChain(pegoutCreationRskTx.getHash().getBytes(), - blockStore)).thenReturn(Optional.of( - pegoutCreationRskTxInfo)); - ReleaseCreationInformationGetter releaseCreationInformationGetter = new ReleaseCreationInformationGetter( receiptStore, blockStore @@ -633,4 +478,37 @@ void getTxInfoToSign_whenRejectedPeginHasPegoutCreatedEvent_returnsOk( assertArrayEquals(expectedOutpointValues.toArray(), pegoutCreationInfo.getUtxoOutpointValues().toArray()); } + + private static List getTxInfoToSignArgProvider() { + List arguments = new ArrayList<>(); + + for (HSMVersion hsmVersion : HSMVersion.values()) { + arguments.add( + Arguments.of(hsmVersion.getNumber(), Hex.decode("00"), + Collections.singletonList(Coin.ZERO))); + arguments.add( + Arguments.of(hsmVersion.getNumber(), Hex.decode("01"), + Collections.singletonList(Coin.SATOSHI))); + // 50_000_000 = FE80F0FA02, 75_000_000 = FEC0687804, 100_000_000 = FE00E1F505 + arguments.add( + Arguments.of(hsmVersion.getNumber(), Hex.decode("FE80F0FA02FEC0687804FE00E1F505"), + coinListOf(50_000_000, 75_000_000, 100_000_000))); + arguments.add(Arguments.of(hsmVersion.getNumber(), Hex.decode("FFFFFFFFFFFFFFFF7F"), + coinListOf(Long.MAX_VALUE))); + } + + return arguments; + } + + private void addCommonPegoutLogs(List logs, BtcTransaction pegoutBtcTx, + byte[] serializedOutpointValues) { + Coin pegoutAmount = mock(Coin.class); + LogInfo releaseRequestedLog = createReleaseRequestedLog(pegoutCreationRskTx.getHash(), + pegoutBtcTx.getHash(), pegoutAmount); + logs.add(releaseRequestedLog); + + LogInfo pegoutTransactionCreatedLog = createPegoutTransactionCreatedLog( + pegoutBtcTx.getHash(), serializedOutpointValues); + logs.add(pegoutTransactionCreatedLog); + } } From f6585d2bc146c8a8fdaf9975e9cec141853061bb Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Mon, 15 Jul 2024 21:33:18 -0400 Subject: [PATCH 6/9] Set mainnet as default value for networkParameter field --- .../rsk/federate/bitcoin/BtcTransactionBuilder.java | 12 ++++++------ .../hsm/client/PowHSMSigningClientBtcTest.java | 2 +- .../hsm/message/PowHSMSignerMessageBuilderTest.java | 2 +- .../signing/hsm/message/PowHSMSignerMessageTest.java | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java index 7e1fd4e1..3e91076b 100644 --- a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java +++ b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java @@ -16,13 +16,14 @@ public final class BtcTransactionBuilder { - private final NetworkParameters networkParameters; + private NetworkParameters networkParameters = NetworkParameters.fromID(NetworkParameters.ID_MAINNET); private final Map transactionWitnesses = new HashMap<>(); private final List inputs = new ArrayList<>(); private final List outputs = new ArrayList<>(); - public BtcTransactionBuilder(NetworkParameters networkParameters) { + public BtcTransactionBuilder withNetworkParameters(NetworkParameters networkParameters) { this.networkParameters = networkParameters; + return this; } public BtcTransactionBuilder withInput(TransactionInput transactionInput) { @@ -30,10 +31,9 @@ public BtcTransactionBuilder withInput(TransactionInput transactionInput) { return this; } - public BtcTransactionBuilder withInput(TransactionOutput transactionOutput) { - TransactionInput transactionInput = new TransactionInput(networkParameters, null, - new byte[]{}, transactionOutput.getOutPointFor(), transactionOutput.getValue()); - inputs.add(transactionInput); + public BtcTransactionBuilder withInputFromOutput(TransactionOutput transactionOutput) { + inputs.add(new InputBuilder(transactionOutput.getValue()).withOutpointIndex( + transactionOutput.getIndex()).build()); return this; } diff --git a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java index 211a125d..47c0b500 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java @@ -423,7 +423,7 @@ private void addCommonPegoutLogs(List logs, BtcTransaction pegoutBtcTx) private BtcTransaction createPegout(List outpointValues, List
destinationAddresses, boolean segwit) { - BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); + BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(); // TODO: improve this method to create a more realistic btc segwit transaction // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java index b229c3c9..860abc63 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java @@ -381,7 +381,7 @@ private void assertSignerMessage(SignerMessage actualSignerMessage, BtcTransacti private BtcTransaction createPegout(List outpointValues, List
destinationAddresses, boolean segwit) { - BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); + BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(); // TODO: improve this test to create a more realistic btc segwit transaction // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java index d2d1ec43..937747cf 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java @@ -259,7 +259,7 @@ void getMessageToSign_whenSighashSegwitMode_ok() { } private BtcTransaction createSegwitPegout(List outpointValues) { - BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(btcMainnetParams); + BtcTransactionBuilder btcTransactionBuilder = new BtcTransactionBuilder(); // TODO: improve this method to create a more realistic btc segwit transaction // once {@link SignerMessageBuilder#getSigHashByInputIndex(int)} is refactored to support segwit From ec49ce2ec3ce428a9b0ec7335d076c44bc9b4266 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Tue, 16 Jul 2024 09:32:36 -0400 Subject: [PATCH 7/9] Reorganize txInput creation and assign --- .../signing/hsm/client/PowHSMSigningClientBtcTest.java | 10 +++++++--- .../hsm/message/PowHSMSignerMessageBuilderTest.java | 10 +++++++--- .../signing/hsm/message/PowHSMSignerMessageTest.java | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java index 47c0b500..f47c3185 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java @@ -37,6 +37,7 @@ import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; +import co.rsk.bitcoinj.core.TransactionInput; import co.rsk.bitcoinj.core.TransactionWitness; import co.rsk.bitcoinj.script.Script; import co.rsk.core.RskAddress; @@ -438,11 +439,14 @@ private BtcTransaction createPegout(List outpointValues, Address destinationAddress = destinationAddresses.get( inputIndex % destinationAddresses.size()); + TransactionInput txInput = btcTransactionBuilder.new InputBuilder( + outpointValue).withOutpointIndex( + inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation).build(); + btcTransactionBuilder .withInput( - btcTransactionBuilder.new InputBuilder(outpointValue).withOutpointIndex( - inputIndex) - .withScriptSig(inputScriptThatSpendsFromTheFederation).build() + txInput ) .withOutput(amountToSend, destinationAddress); diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java index 860abc63..f4ec0441 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java @@ -18,6 +18,7 @@ import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; +import co.rsk.bitcoinj.core.TransactionInput; import co.rsk.bitcoinj.core.TransactionWitness; import co.rsk.bitcoinj.script.Script; import co.rsk.core.RskAddress; @@ -396,11 +397,14 @@ private BtcTransaction createPegout(List outpointValues, Address destinationAddress = destinationAddresses.get( inputIndex % destinationAddresses.size()); + TransactionInput txInput = btcTransactionBuilder.new InputBuilder( + outpointValue).withOutpointIndex( + inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation).build(); + btcTransactionBuilder .withInput( - btcTransactionBuilder.new InputBuilder(outpointValue).withOutpointIndex( - inputIndex) - .withScriptSig(inputScriptThatSpendsFromTheFederation).build() + txInput ) .withOutput(amountToSend, destinationAddress); diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java index 937747cf..54737cc2 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java @@ -34,6 +34,7 @@ import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; +import co.rsk.bitcoinj.core.TransactionInput; import co.rsk.bitcoinj.core.TransactionWitness; import co.rsk.bitcoinj.params.RegTestParams; import co.rsk.bitcoinj.script.Script; @@ -277,11 +278,14 @@ private BtcTransaction createSegwitPegout(List outpointValues) { Address destinationAddress = destinationAddresses.get( inputIndex % destinationAddresses.size()); + TransactionInput txInput = btcTransactionBuilder.new InputBuilder( + outpointValue).withOutpointIndex( + inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation).build(); + btcTransactionBuilder .withInput( - btcTransactionBuilder.new InputBuilder(outpointValue).withOutpointIndex( - inputIndex) - .withScriptSig(inputScriptThatSpendsFromTheFederation).build() + txInput ) .withOutput(amountToSend, destinationAddress); From 3c30320c6b8235022586ff3b8ba10f24608134ea Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Wed, 17 Jul 2024 12:10:12 -0400 Subject: [PATCH 8/9] Make InputBuilder constructor private and now instance is created using BtcTransactionBuilder helper method --- .../rsk/federate/bitcoin/BtcTransactionBuilder.java | 13 ++++++++++--- .../hsm/client/PowHSMSigningClientBtcTest.java | 8 ++++---- .../hsm/message/PowHSMSignerMessageBuilderTest.java | 8 ++++---- .../hsm/message/PowHSMSignerMessageTest.java | 8 ++++---- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java index 3e91076b..235c9c22 100644 --- a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java +++ b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java @@ -21,6 +21,10 @@ public final class BtcTransactionBuilder { private final List inputs = new ArrayList<>(); private final List outputs = new ArrayList<>(); + public InputBuilder createInputBuilder() { + return new InputBuilder(); + } + public BtcTransactionBuilder withNetworkParameters(NetworkParameters networkParameters) { this.networkParameters = networkParameters; return this; @@ -32,7 +36,7 @@ public BtcTransactionBuilder withInput(TransactionInput transactionInput) { } public BtcTransactionBuilder withInputFromOutput(TransactionOutput transactionOutput) { - inputs.add(new InputBuilder(transactionOutput.getValue()).withOutpointIndex( + inputs.add(new InputBuilder().withAmount(transactionOutput.getValue()).withOutpointIndex( transactionOutput.getIndex()).build()); return this; } @@ -73,12 +77,15 @@ private void addOutputs(BtcTransaction btcTransaction) { public class InputBuilder { - private final Coin amount; + private Coin amount; private Script scriptSig; private int outpointIndex = 0; - public InputBuilder(Coin amount) { + private InputBuilder() { } + + public InputBuilder withAmount(Coin amount) { this.amount = amount; + return this; } public InputBuilder withScriptSig(Script scriptSig) { diff --git a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java index f47c3185..00e96536 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java @@ -439,10 +439,10 @@ private BtcTransaction createPegout(List outpointValues, Address destinationAddress = destinationAddresses.get( inputIndex % destinationAddresses.size()); - TransactionInput txInput = btcTransactionBuilder.new InputBuilder( - outpointValue).withOutpointIndex( - inputIndex) - .withScriptSig(inputScriptThatSpendsFromTheFederation).build(); + TransactionInput txInput = btcTransactionBuilder.createInputBuilder() + .withAmount(outpointValue).withOutpointIndex(inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation) + .build(); btcTransactionBuilder .withInput( diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java index f4ec0441..e928fce4 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java @@ -397,10 +397,10 @@ private BtcTransaction createPegout(List outpointValues, Address destinationAddress = destinationAddresses.get( inputIndex % destinationAddresses.size()); - TransactionInput txInput = btcTransactionBuilder.new InputBuilder( - outpointValue).withOutpointIndex( - inputIndex) - .withScriptSig(inputScriptThatSpendsFromTheFederation).build(); + TransactionInput txInput = btcTransactionBuilder.createInputBuilder() + .withAmount(outpointValue).withOutpointIndex(inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation) + .build(); btcTransactionBuilder .withInput( diff --git a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java index 54737cc2..a11b70bd 100644 --- a/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java +++ b/src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageTest.java @@ -278,10 +278,10 @@ private BtcTransaction createSegwitPegout(List outpointValues) { Address destinationAddress = destinationAddresses.get( inputIndex % destinationAddresses.size()); - TransactionInput txInput = btcTransactionBuilder.new InputBuilder( - outpointValue).withOutpointIndex( - inputIndex) - .withScriptSig(inputScriptThatSpendsFromTheFederation).build(); + TransactionInput txInput = btcTransactionBuilder.createInputBuilder() + .withAmount(outpointValue).withOutpointIndex(inputIndex) + .withScriptSig(inputScriptThatSpendsFromTheFederation) + .build(); btcTransactionBuilder .withInput( From ce199e90cc8aee04e05caca95bec72571b8150a7 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Wed, 17 Jul 2024 16:48:40 -0400 Subject: [PATCH 9/9] Renamed variable. --- .../java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java index 235c9c22..29f2041b 100644 --- a/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java +++ b/src/test/java/co/rsk/federate/bitcoin/BtcTransactionBuilder.java @@ -63,10 +63,10 @@ public BtcTransaction build() { private void addInputs(BtcTransaction btcTransaction) { inputs.forEach(transactionInput -> { - int idx = btcTransaction.getInputs().size(); + int inputIndex = btcTransaction.getInputs().size(); btcTransaction.addInput(transactionInput); - if (transactionWitnesses.containsKey(idx)) { - btcTransaction.setWitness(idx, transactionWitnesses.get(idx)); + if (transactionWitnesses.containsKey(inputIndex)) { + btcTransaction.setWitness(inputIndex, transactionWitnesses.get(inputIndex)); } }); }