Skip to content

Commit

Permalink
feat(peg): refactor min peg value tests
Browse files Browse the repository at this point in the history
  • Loading branch information
apancorb committed Aug 15, 2024
1 parent da54401 commit 09c7677
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 153 deletions.
189 changes: 189 additions & 0 deletions rskj-core/src/test/java/co/rsk/peg/MinimumPegValueTest.java
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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 {

Check notice

Code scanning / CodeQL

Unused classes and interfaces Note test

Unused class: MinimumPegValueTest is not referenced within this codebase. If not used as an external API it should be removed.
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<ReleaseRequestQueue.Entry> entries = Arrays.asList(
createTestEntry(1000, minPegoutValue));
// list of utxos that contains one utxo with the minimum peg-in value
List<UTXO> utxos = Arrays.asList(
new UTXO(getUTXOHash("utxo"), 0, minPeginValue, 0, false, fed.getP2SHScript()));
// the federation wallet, with flyover support
Wallet fedWallet = BridgeUtils.getFederationSpendWallet(
Context.getOrCreate(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<ReleaseRequestQueue.Entry> 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<UTXO> 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(
Context.getOrCreate(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<ReleaseRequestQueue.Entry> 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<UTXO> 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(
Context.getOrCreate(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<Arguments> 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);
}
}
153 changes: 0 additions & 153 deletions rskj-core/src/test/java/co/rsk/peg/ReleaseTransactionBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -874,159 +874,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<ReleaseRequestQueue.Entry> entries = Arrays.asList(
createTestEntry(1000, minPegoutValue));
// list of utxos that contains one utxo with a value of 1 BTC
List<UTXO> 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<ReleaseRequestQueue.Entry> 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<UTXO> 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<ReleaseRequestQueue.Entry> 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<UTXO> 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<Arguments> 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);
Expand Down

0 comments on commit 09c7677

Please sign in to comment.