Skip to content

Commit

Permalink
Merge pull request #2798 from rsksmart/refund-pre-rskip427
Browse files Browse the repository at this point in the history
When rejected pegout is refunded, post RSKIP427 refund the total value sent in weis
  • Loading branch information
marcos-iov authored Oct 15, 2024
2 parents 46557c4 + 4837fa4 commit 084e84e
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 12 deletions.
9 changes: 8 additions & 1 deletion rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -850,10 +850,17 @@ private void refundAndEmitRejectEvent(
senderAddress,
reason
);

// Prior to RSKIP427, the value was converted to BTC before doing the refund
// This could cause the original value to be rounded down to fit in satoshis value
co.rsk.core.Coin refundValue = activations.isActive(RSKIP427) ?
releaseRequestedValue :
co.rsk.core.Coin.fromBitcoin(releaseRequestedValue.toBitcoin());

rskRepository.transfer(
PrecompiledContracts.BRIDGE_ADDR,
senderAddress,
releaseRequestedValue
refundValue
);
emitRejectEvent(releaseRequestedValue, senderAddress, reason);
}
Expand Down
72 changes: 61 additions & 11 deletions rskj-core/src/test/java/co/rsk/peg/BridgeSupportReleaseBtcTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.ethereum.vm.program.InternalTransaction;
import org.ethereum.vm.program.Program;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class BridgeSupportReleaseBtcTest {
Expand Down Expand Up @@ -1260,7 +1261,8 @@ private void testPegoutMinimumWithFeeVerificationRejectedByFeeAboveValue(
}

@Test
void low_amount_release_request_rejected_before_lovell_value_in_satoshis() throws IOException {
@DisplayName("A rejected pegout due to low amount. Pre lovell, the value is rounded down to the nearest satoshi. Both in the emitted event and the value refunded.")
void low_amount_release_request_rejected_before_lovell() throws IOException {
ActivationConfig.ForBlock arrowheadActivations = ActivationConfigsForTest.arrowhead631().forBlock(0L);

List<LogInfo> logInfo = new ArrayList<>();
Expand All @@ -1274,15 +1276,23 @@ void low_amount_release_request_rejected_before_lovell_value_in_satoshis() throw

Coin belowPegoutMinimumValue = BRIDGE_CONSTANTS.getMinimumPegoutTxValue().minus(Coin.SATOSHI);
co.rsk.core.Coin pegoutRequestValue = co.rsk.core.Coin.fromBitcoin(belowPegoutMinimumValue);
// Add some extra weis to the value, but less than 1 satoshi.
// To ensure that the pegout value is rounded down to fit in satoshis.
co.rsk.core.Coin oneSatoshiInWeis = co.rsk.core.Coin.fromBitcoin(Coin.SATOSHI);
co.rsk.core.Coin oneWei = co.rsk.core.Coin.valueOf(Denomination.WEI.longValue());
co.rsk.core.Coin extraWeis = oneSatoshiInWeis.subtract(oneWei);
pegoutRequestValue = pegoutRequestValue.add(extraWeis);

bridgeSupport.releaseBtc(buildReleaseRskTx(pegoutRequestValue));

RskAddress senderAddress = new RskAddress(SENDER.getAddress());

// Pre lovell, the refund should be rounded down to the nearest satoshi.
co.rsk.core.Coin expectedRefundValue = pegoutRequestValue.subtract(extraWeis);
verify(repository, times(1)).transfer(
BRIDGE_ADDRESS,
senderAddress,
pegoutRequestValue
expectedRefundValue
);

assertEquals(0, provider.getReleaseRequestQueue().getEntries().size());
Expand All @@ -1299,12 +1309,15 @@ void low_amount_release_request_rejected_before_lovell_value_in_satoshis() throw
CallTransaction.Function event = BridgeEvents.RELEASE_REQUEST_REJECTED.getEvent();
assertArrayEquals(event.encodeSignatureLong(), firstLog.getTopics().get(0).getData());

// Same case in the log, the value should be rounded down to the nearest satoshi.
BigInteger amount = (BigInteger) event.decodeEventData(firstLog.getData())[0];
assertEquals(pegoutRequestValue.toBitcoin().longValue(), amount.longValue());
long expectedAmountLogged = expectedRefundValue.toBitcoin().longValue();
assertEquals(expectedAmountLogged, amount.longValue());
}

@Test
void low_amount_release_request_rejected_after_lovell_value_in_weis() throws IOException {
@DisplayName("A rejected pegout due to low amount. Post lovell, the pegout value is preserved in weis. Both in the emitted event and the value refunded.")
void low_amount_release_request_rejected_after_lovell() throws IOException {
List<LogInfo> logInfo = new ArrayList<>();
eventLogger = spy(new BridgeEventLoggerImpl(
BRIDGE_CONSTANTS,
Expand Down Expand Up @@ -1346,7 +1359,8 @@ void low_amount_release_request_rejected_after_lovell_value_in_weis() throws IOE
}

@Test
void contract_caller_release_request_rejected_before_lovell_value_in_satoshis() throws IOException {
@DisplayName("A pegout from a contract is rejected. Pre lovell, the pegout value is rounded down to the nearest satoshi. Both in the emitted event and the value refunded.")
void contract_caller_release_request_rejected_before_lovell() throws IOException {
ActivationConfig.ForBlock arrowheadActivations = ActivationConfigsForTest.arrowhead631().forBlock(0L);

List<LogInfo> logInfo = new ArrayList<>();
Expand All @@ -1359,11 +1373,18 @@ void contract_caller_release_request_rejected_before_lovell_value_in_satoshis()
bridgeSupport = initBridgeSupport(eventLogger, arrowheadActivations);

co.rsk.core.Coin pegoutRequestValue = co.rsk.core.Coin.fromBitcoin(BRIDGE_CONSTANTS.getMinimumPegoutTxValue());
// Add some extra weis to the value, but less than 1 satoshi.
// To ensure that the pegout value is rounded down to fit in satoshis.
co.rsk.core.Coin oneSatoshiInWeis = co.rsk.core.Coin.fromBitcoin(Coin.SATOSHI);
co.rsk.core.Coin oneWei = co.rsk.core.Coin.valueOf(Denomination.WEI.longValue());
co.rsk.core.Coin extraWeis = oneSatoshiInWeis.subtract(oneWei);
pegoutRequestValue = pegoutRequestValue.add(extraWeis);

bridgeSupport.releaseBtc(buildReleaseRskTx_fromContract(pegoutRequestValue));

RskAddress senderAddress = new RskAddress(SENDER.getAddress());

// No refund is made to a contract
verify(repository, never()).transfer(any(), any(), any());

assertEquals(0, provider.getReleaseRequestQueue().getEntries().size());
Expand All @@ -1381,11 +1402,15 @@ void contract_caller_release_request_rejected_before_lovell_value_in_satoshis()
assertArrayEquals(event.encodeSignatureLong(), firstLog.getTopics().get(0).getData());

BigInteger amount = (BigInteger) event.decodeEventData(firstLog.getData())[0];
assertEquals(pegoutRequestValue.toBitcoin().longValue(), amount.longValue());
// Pre lovell, the logged value should be rounded down to the nearest satoshi.
co.rsk.core.Coin expectedLoggedValue = pegoutRequestValue.subtract(extraWeis);
long expectedAmountLogged = expectedLoggedValue.toBitcoin().longValue();
assertEquals(expectedAmountLogged, amount.longValue());
}

@Test
void contract_caller_release_request_rejected_after_lovell_value_in_weis() throws IOException {
@DisplayName("A pegout from a contract is rejected. Post lovell, the pegout value is preserved in weis. Both in the emitted event and the value refunded.")
void contract_caller_release_request_rejected_after_lovell() throws IOException {
List<LogInfo> logInfo = new ArrayList<>();
eventLogger = spy(new BridgeEventLoggerImpl(
BRIDGE_CONSTANTS,
Expand All @@ -1396,11 +1421,18 @@ void contract_caller_release_request_rejected_after_lovell_value_in_weis() throw
bridgeSupport = initBridgeSupport(eventLogger, ACTIVATIONS_ALL);

co.rsk.core.Coin pegoutRequestValue = co.rsk.core.Coin.fromBitcoin(BRIDGE_CONSTANTS.getMinimumPegoutTxValue());
// Add some extra weis to the value, but less than 1 satoshi.
// To ensure that the pegout value is rounded down to fit in satoshis.
co.rsk.core.Coin oneSatoshiInWeis = co.rsk.core.Coin.fromBitcoin(Coin.SATOSHI);
co.rsk.core.Coin oneWei = co.rsk.core.Coin.valueOf(Denomination.WEI.longValue());
co.rsk.core.Coin extraWeis = oneSatoshiInWeis.subtract(oneWei);
pegoutRequestValue = pegoutRequestValue.add(extraWeis);

bridgeSupport.releaseBtc(buildReleaseRskTx_fromContract(pegoutRequestValue));

RskAddress senderAddress = new RskAddress(SENDER.getAddress());

// No refund is made to a contract
verify(repository, never()).transfer(any(), any(), any());

assertEquals(0, provider.getReleaseRequestQueue().getEntries().size());
Expand All @@ -1422,7 +1454,8 @@ void contract_caller_release_request_rejected_after_lovell_value_in_weis() throw
}

@Test
void fee_above_value_release_request_rejected_before_lovell_value_in_satoshis() throws IOException {
@DisplayName("A pegout rejected due to high fees. Pre lovell, the pegout value is rounded down to the nearest satoshi. Both in the emitted event and the value refunded.")
void fee_above_value_release_request_rejected_before_lovell() throws IOException {
ActivationConfig.ForBlock arrowheadActivations = ActivationConfigsForTest.arrowhead631().forBlock(0L);

List<LogInfo> logInfo = new ArrayList<>();
Expand All @@ -1447,15 +1480,23 @@ void fee_above_value_release_request_rejected_before_lovell_value_in_satoshis()

Coin pegoutRequestValueWithGapAboveFee = minValueWithGapAboveFee.minus(Coin.SATOSHI);
co.rsk.core.Coin pegoutRequestValue = co.rsk.core.Coin.fromBitcoin(pegoutRequestValueWithGapAboveFee);
// Add some extra weis to the value, but less than 1 satoshi.
// To ensure that the pegout value is rounded down to fit in satoshis.
co.rsk.core.Coin oneSatoshiInWeis = co.rsk.core.Coin.fromBitcoin(Coin.SATOSHI);
co.rsk.core.Coin oneWei = co.rsk.core.Coin.valueOf(Denomination.WEI.longValue());
co.rsk.core.Coin extraWeis = oneSatoshiInWeis.subtract(oneWei);
pegoutRequestValue = pegoutRequestValue.add(extraWeis);

bridgeSupport.releaseBtc(buildReleaseRskTx(pegoutRequestValue));

RskAddress senderAddress = new RskAddress(SENDER.getAddress());

// Pre lovell, the refund should be rounded down to the nearest satoshi.
co.rsk.core.Coin expectedRefundValue = pegoutRequestValue.subtract(extraWeis);
verify(repository, times(1)).transfer(
BRIDGE_ADDRESS,
senderAddress,
pegoutRequestValue
expectedRefundValue
);

assertEquals(0, provider.getReleaseRequestQueue().getEntries().size());
Expand All @@ -1472,12 +1513,15 @@ void fee_above_value_release_request_rejected_before_lovell_value_in_satoshis()
CallTransaction.Function event = BridgeEvents.RELEASE_REQUEST_REJECTED.getEvent();
assertArrayEquals(event.encodeSignatureLong(), firstLog.getTopics().get(0).getData());

// Same case in the log, the value should be rounded down to the nearest satoshi.
BigInteger amount = (BigInteger) event.decodeEventData(firstLog.getData())[0];
assertEquals(pegoutRequestValue.toBitcoin().longValue(), amount.longValue());
long expectedAmountLogged = expectedRefundValue.toBitcoin().longValue();
assertEquals(expectedAmountLogged, amount.longValue());
}

@Test
void fee_above_value_release_request_rejected_after_lovell_value_in_weis() throws IOException {
@DisplayName("A pegout rejected due to high fees. Post lovell, the pegout value is preserved in weis. Both in the emitted event and the value refunded.")
void fee_above_value_release_request_rejected_after_lovell() throws IOException {
List<LogInfo> logInfo = new ArrayList<>();
eventLogger = spy(new BridgeEventLoggerImpl(
BRIDGE_CONSTANTS,
Expand All @@ -1500,6 +1544,12 @@ void fee_above_value_release_request_rejected_after_lovell_value_in_weis() throw

Coin pegoutRequestValueWithGapAboveFee = minValueWithGapAboveFee.minus(Coin.SATOSHI);
co.rsk.core.Coin pegoutRequestValue = co.rsk.core.Coin.fromBitcoin(pegoutRequestValueWithGapAboveFee);
// Add some extra weis to the value, but less than 1 satoshi.
// To ensure that the pegout value is rounded down to fit in satoshis.
co.rsk.core.Coin oneSatoshiInWeis = co.rsk.core.Coin.fromBitcoin(Coin.SATOSHI);
co.rsk.core.Coin oneWei = co.rsk.core.Coin.valueOf(Denomination.WEI.longValue());
co.rsk.core.Coin extraWeis = oneSatoshiInWeis.subtract(oneWei);
pegoutRequestValue = pegoutRequestValue.add(extraWeis);

bridgeSupport.releaseBtc(buildReleaseRskTx(pegoutRequestValue));

Expand Down

0 comments on commit 084e84e

Please sign in to comment.