diff --git a/rskj-core/src/test/java/co/rsk/peg/MinimumPegValueTest.java b/rskj-core/src/test/java/co/rsk/peg/MinimumPegValueTest.java
new file mode 100644
index 00000000000..28a71134868
--- /dev/null
+++ b/rskj-core/src/test/java/co/rsk/peg/MinimumPegValueTest.java
@@ -0,0 +1,189 @@
+/*
+ * This file is part of RskJ
+ * Copyright (C) 2024 RSK Labs Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+package co.rsk.peg;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import co.rsk.bitcoinj.core.Address;
+import co.rsk.bitcoinj.core.BtcECKey;
+import co.rsk.bitcoinj.core.Coin;
+import co.rsk.bitcoinj.core.Context;
+import co.rsk.bitcoinj.core.NetworkParameters;
+import co.rsk.bitcoinj.core.Sha256Hash;
+import co.rsk.bitcoinj.core.UTXO;
+import co.rsk.bitcoinj.wallet.Wallet;
+import co.rsk.peg.constants.BridgeConstants;
+import co.rsk.peg.constants.BridgeMainNetConstants;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+import co.rsk.peg.federation.*;
+import org.ethereum.config.blockchain.upgrades.ActivationConfig;
+import org.ethereum.config.blockchain.upgrades.ConsensusRule;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class MinimumPegValueTest {
+ private ActivationConfig.ForBlock activations;
+ private NetworkParameters networkParameters;
+ private BridgeConstants bridgeMainNetConstants;
+
+ @BeforeEach
+ void setup() {
+ activations = mock(ActivationConfig.ForBlock.class);
+ bridgeMainNetConstants = BridgeMainNetConstants.getInstance();
+ networkParameters = bridgeMainNetConstants.getBtcParams();
+ }
+
+ @ParameterizedTest()
+ @MethodSource("providePegMinimumParameters")
+ void checkProposedPegValues_whenOneInputofMinPeginValueAndOneOutputOfMinPegoutValue_shouldBuildTransactionSuccessfully(
+ Coin feePerKb, Coin minPeginValue, Coin minPegoutValue) {
+ // build federation
+ Federation fed = new P2shErpFederationBuilder().withNetworkParameters(networkParameters).build();
+ // list of peg-out requests with the given minimum peg-out value
+ List entries = Arrays.asList(
+ createTestEntry(1000, minPegoutValue));
+ // list of utxos that contains one utxo with the minimum peg-in value
+ List utxos = Arrays.asList(
+ new UTXO(getUTXOHash("utxo"), 0, minPeginValue, 0, false, fed.getP2SHScript()));
+ // the federation wallet, with flyover support
+ Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
+ new Context(networkParameters),
+ fed,
+ utxos,
+ true,
+ mock(BridgeStorageProvider.class));
+ // build release transaction builder for current fed
+ when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(true);
+ ReleaseTransactionBuilder releaseTransactionBuilder = new ReleaseTransactionBuilder(
+ networkParameters,
+ fedWallet,
+ fed.getAddress(),
+ feePerKb,
+ activations);
+
+ // build batch peg-out transaction
+ ReleaseTransactionBuilder.BuildResult result = releaseTransactionBuilder.buildBatchedPegouts(entries);
+
+ // the proposed feePerKb and peg values are compatible
+ assertEquals(ReleaseTransactionBuilder.Response.SUCCESS, result.getResponseCode());
+ }
+
+ @ParameterizedTest()
+ @MethodSource("providePegMinimumParameters")
+ void checkProposedPegValues_whenOneInputOfTenTimesMinPeginValueAndTenOutputsOfMinPegoutValue_shouldBuildTransactionSuccessfully(
+ Coin feePerKb, Coin minPeginValue, Coin minPegoutValue) {
+ // build federation
+ Federation fed = new P2shErpFederationBuilder().withNetworkParameters(networkParameters).build();
+ // list of peg-out requests with the given minimum peg-out value
+ List entries = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ entries.add(createTestEntry(1000 + i, minPegoutValue));
+ }
+ // list of utxos that contains one utxo with the minimum peg-in value
+ List utxos = Arrays.asList(
+ new UTXO(getUTXOHash("utxo"), 0, minPeginValue.times(10), 0, false, fed.getP2SHScript()));
+ // the federation wallet, with flyover support
+ Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
+ new Context(networkParameters),
+ fed,
+ utxos,
+ true,
+ mock(BridgeStorageProvider.class));
+ // build release transaction builder for current fed
+ when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(true);
+ ReleaseTransactionBuilder releaseTransactionBuilder = new ReleaseTransactionBuilder(
+ networkParameters,
+ fedWallet,
+ fed.getAddress(),
+ feePerKb,
+ activations);
+
+ // build batch peg-out transaction
+ ReleaseTransactionBuilder.BuildResult result = releaseTransactionBuilder.buildBatchedPegouts(entries);
+
+ // the proposed feePerKb and peg values are compatible
+ assertEquals(ReleaseTransactionBuilder.Response.SUCCESS, result.getResponseCode());
+ }
+
+ @ParameterizedTest()
+ @MethodSource("providePegMinimumParameters")
+ void checkProposedPegValues_whenTenInputOfMinPeginValueAndTenOutputsOfMinPegoutValue_shouldBuildTransactionSuccessfully(
+ Coin feePerKb, Coin minPeginValue, Coin minPegoutValue) {
+ // build federation
+ Federation fed = new P2shErpFederationBuilder().withNetworkParameters(networkParameters).build();
+ // list of peg-out requests with the given minimum peg-out value
+ List entries = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ entries.add(createTestEntry(1000 + i, minPegoutValue));
+ }
+ // list of utxos that contains one utxo with the minimum peg-in value
+ List utxos = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ utxos.add(
+ new UTXO(getUTXOHash("utxo" + i), 0, minPeginValue, 0, false, fed.getP2SHScript()));
+ }
+ // the federation wallet, with flyover support
+ Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
+ new Context(networkParameters),
+ fed,
+ utxos,
+ true,
+ mock(BridgeStorageProvider.class));
+ // build release transaction builder for current fed
+ when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(true);
+ ReleaseTransactionBuilder releaseTransactionBuilder = new ReleaseTransactionBuilder(
+ networkParameters,
+ fedWallet,
+ fed.getAddress(),
+ feePerKb,
+ activations);
+
+ // build batch peg-out transaction
+ ReleaseTransactionBuilder.BuildResult result = releaseTransactionBuilder.buildBatchedPegouts(entries);
+
+ // the proposed feePerKb and peg values are compatible
+ assertEquals(ReleaseTransactionBuilder.Response.SUCCESS, result.getResponseCode());
+ }
+
+ private static Stream providePegMinimumParameters() {
+ return Stream.of(
+ Arguments.of(Coin.valueOf(24_000L), Coin.valueOf(100_000L), Coin.valueOf(80_000L)));
+ }
+
+ private Address getAddress(int pk) {
+ return BtcECKey.fromPrivate(BigInteger.valueOf(pk))
+ .toAddress(NetworkParameters.fromID(NetworkParameters.ID_MAINNET));
+ }
+
+ private Sha256Hash getUTXOHash(String generator) {
+ return Sha256Hash.of(generator.getBytes(StandardCharsets.UTF_8));
+ }
+
+ private ReleaseRequestQueue.Entry createTestEntry(int addressPk, Coin amount) {
+ return new ReleaseRequestQueue.Entry(getAddress(addressPk), amount);
+ }
+}
diff --git a/rskj-core/src/test/java/co/rsk/peg/ReleaseTransactionBuilderTest.java b/rskj-core/src/test/java/co/rsk/peg/ReleaseTransactionBuilderTest.java
index db14d050fc6..9b162326b33 100644
--- a/rskj-core/src/test/java/co/rsk/peg/ReleaseTransactionBuilderTest.java
+++ b/rskj-core/src/test/java/co/rsk/peg/ReleaseTransactionBuilderTest.java
@@ -37,16 +37,13 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Stream;
+
import co.rsk.peg.federation.*;
import org.ethereum.config.blockchain.upgrades.ActivationConfig;
import org.ethereum.config.blockchain.upgrades.ConsensusRule;
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;
-import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
@@ -874,159 +871,6 @@ void test_VerifyTXFeeIsSpentEquallyForBatchedPegouts_three_pegouts() {
assertEquals(inputsValue.minus(totalPegoutAmount), changeOutput.getValue());
}
- @ParameterizedTest()
- @MethodSource("providePegoutMinimumParameters")
- void buildBatchedPegouts_whenSpendableUXTO_shouldBuildTransactionSuccessfully(Coin feePerKb, Coin minPegoutValue) {
- // build federation with 9 members
- Integer[] memberPKs = new Integer[]{ 100, 200, 300, 400, 500, 600, 700, 800, 900 };
- Federation fed = FederationTestUtils.getFederation(memberPKs);
- // list of peg-out requests with the given minimum peg-out value
- List entries = Arrays.asList(
- createTestEntry(1000, minPegoutValue));
- // list of utxos that contains one utxo with a value of 1 BTC
- List utxos = Arrays.asList(
- new UTXO(mockUTXOHash("utxo"), 0, Coin.COIN, 0, false, fed.getP2SHScript()));
- // the federation wallet, without flyover support
- Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
- Context.getOrCreate(networkParameters),
- fed,
- utxos,
- false,
- mock(BridgeStorageProvider.class)
- );
- // build release transaction builder for current fed
- when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(true);
- ReleaseTransactionBuilder releaseTransactionBuilder = new ReleaseTransactionBuilder(
- networkParameters,
- fedWallet,
- fed.getAddress(),
- feePerKb,
- activations
- );
-
- // build batch peg-out transaction
- ReleaseTransactionBuilder.BuildResult result = releaseTransactionBuilder.buildBatchedPegouts(entries);
-
- // the proposed feePerKb and minPegoutValue are compatible
- assertEquals(ReleaseTransactionBuilder.Response.SUCCESS, result.getResponseCode());
- }
-
- @ParameterizedTest()
- @MethodSource("providePegoutMinimumParameters")
- void buildBatchedPegouts_whenUnspendableUXTOs_shouldBuildTransactionSuccessfully(Coin feePerKb, Coin minPegoutValue) {
- // build federation with 9 members
- Integer[] memberPKs = new Integer[]{ 100, 200, 300, 400, 500, 600, 700, 800, 900 };
- Federation fed = FederationTestUtils.getFederation(memberPKs);
- // list of peg-out requests with the given minimum peg-out value
- List entries = Arrays.asList(
- createTestEntry(1000, minPegoutValue));
- // this is a bit hacky, but this is to ensure the minPegoutValue chosen
- // is not dust with the current wallet implementation. Either way this would be
- // checked later on when building the transaction, but I think is good to have this explicitly
- BtcTransaction tx = new BtcTransaction(networkParameters);
- tx.addOutput(entries.get(0).getAmount(), entries.get(0).getDestination());
- assertFalse(tx.getOutput(0).isDust());
- // now we need to calculate the min value that an UTXO can use to be spent. This would be the least
- // amount needed by the change ouput back to the federation address. This could also be the mininum
- // pegin value, but since a change output is also an UTXO that the current wallet implementation can use
- // then we will find and use that value.
- Coin minSpendableUTXOValue = Coin.SATOSHI;
- tx.clearOutputs();
- tx.addOutput(minSpendableUTXOValue, fed.getP2SHScript());
- while (tx.getOutput(0).isDust()) {
- minSpendableUTXOValue = minSpendableUTXOValue.add(Coin.SATOSHI);
- tx.clearOutputs();
- tx.addOutput(minSpendableUTXOValue, fed.getP2SHScript());
- }
- // list of utxos that contains N mimUnspendableUTXO to cover the minPegoutValue
- List utxos = new ArrayList<>();
- Coin[] neededUTXOsInCoin = minPegoutValue.divideAndRemainder(minSpendableUTXOValue.getValue());
- long neededUTXOs = neededUTXOsInCoin[0].getValue();
- if (!neededUTXOsInCoin[1].isZero()) {
- neededUTXOs++;
- }
- for (int i = 0; i < neededUTXOs; i++) {
- utxos.add(
- new UTXO(mockUTXOHash("utxo " + i), 0, minSpendableUTXOValue, 0, false, fed.getP2SHScript()));
- }
- // the federation wallet, without flyover support
- Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
- Context.getOrCreate(networkParameters),
- fed,
- utxos,
- false,
- mock(BridgeStorageProvider.class)
- );
- // build release transaction builder for current fed
- when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(true);
- ReleaseTransactionBuilder releaseTransactionBuilder = new ReleaseTransactionBuilder(
- networkParameters,
- fedWallet,
- fed.getAddress(),
- feePerKb,
- activations
- );
-
- // build batch peg-out transaction
- ReleaseTransactionBuilder.BuildResult result = releaseTransactionBuilder.buildBatchedPegouts(entries);
-
- // the proposed feePerKb and minPegoutValue are compatible
- assertEquals(ReleaseTransactionBuilder.Response.SUCCESS, result.getResponseCode());
- }
-
- @ParameterizedTest()
- @MethodSource("providePegoutMinimumParameters")
- void buildBatchedPegouts_whenTenUXTOs_shouldBuildTransactionSuccessfully(Coin feePerKb, Coin minPegoutValue) {
- // build federation with 9 members
- Integer[] memberPKs = new Integer[]{ 100, 200, 300, 400, 500, 600, 700, 800, 900 };
- Federation fed = FederationTestUtils.getFederation(memberPKs);
- // list of peg-out requests with the given minimum peg-out value
- List entries = Arrays.asList(
- createTestEntry(1000, minPegoutValue));
- // this is a bit hacky, but this is to ensure the minPegoutValue chosen
- // is not dust with the current wallet implementation. Either way this would be
- // checked later on when building the transaction, but I think is good to have this explicitly
- BtcTransaction tx = new BtcTransaction(networkParameters);
- tx.addOutput(entries.get(0).getAmount(), entries.get(0).getDestination());
- assertFalse(tx.getOutput(0).isDust());
- // list of utxos that contains 10 utxos to cover the minPegoutValue
- Coin utxoValue = minPegoutValue.div(10);
- List utxos = new ArrayList<>();
- for (int i = 0; i < 10; i++) {
- utxos.add(
- new UTXO(mockUTXOHash("utxo " + i), 0, utxoValue, 0, false, fed.getP2SHScript()));
- }
- // the federation wallet, without flyover support
- Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
- Context.getOrCreate(networkParameters),
- fed,
- utxos,
- false,
- mock(BridgeStorageProvider.class)
- );
- // build release transaction builder for current fed
- when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(true);
- ReleaseTransactionBuilder releaseTransactionBuilder = new ReleaseTransactionBuilder(
- networkParameters,
- fedWallet,
- fed.getAddress(),
- feePerKb,
- activations
- );
-
- // build batch peg-out transaction
- ReleaseTransactionBuilder.BuildResult result = releaseTransactionBuilder.buildBatchedPegouts(entries);
-
- // the proposed feePerKb and minPegoutValue are compatible
- assertEquals(ReleaseTransactionBuilder.Response.SUCCESS, result.getResponseCode());
- }
-
- private static Stream providePegoutMinimumParameters() {
- return Stream.of(
- Arguments.of(Coin.valueOf(24_000L), Coin.valueOf(180_000L))
- );
- }
-
private void test_buildEmptyWalletTo_ok(boolean isRSKIPActive, int expectedTxVersion)
throws InsufficientMoneyException, UTXOProviderException {
when(activations.isActive(ConsensusRule.RSKIP201)).thenReturn(isRSKIPActive);