Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark invalid pegin as processed #2910

Conversation

nathanieliov
Copy link
Contributor

@nathanieliov nathanieliov commented Jan 2, 2025

Mark rejected pegins as processed once RSKIP459 is active.

Motivation and Context

Peg-in transactions rejected by the Bridge should be marked as processed to ensure that the rejected_pegin event is emitted only once per transaction.

How Has This Been Tested?

Unit tests

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • Tests for the changes have been added (for bug fixes / features)
  • Requires Activation Code (Hard Fork)

@nathanieliov nathanieliov requested a review from a team January 2, 2025 06:06
Copy link

github-actions bot commented Jan 2, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails

Scanned Manifest Files

…jected pegin should be marked as processed

- Update BridgeSupportRegisterBtcTransactionTest to assert rejected pegin are marked as processed since RSKIP459
- Add tests asserting rejected pegin are marked as processed once RSKIP459 is active
@nathanieliov nathanieliov force-pushed the mark-invalid-pegin-as-processed branch from d8f72bb to 208b14f Compare January 2, 2025 19:01
@nathanieliov nathanieliov marked this pull request as ready for review January 2, 2025 19:02
private static final NetworkParameters btcMainnetParams = bridgeMainnetConstants.getBtcParams();
private static final ActivationConfig.ForBlock arrowHeadActivations = ActivationConfigsForTest.arrowhead600().forBlock(0);
private static final ActivationConfig.ForBlock lovellActivations = ActivationConfigsForTest.lovell700().forBlock(0);
/*private static final ActivationConfig.ForBlock activations = ActivationConfigsForTest.all().forBlock(0);*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we ever need this line? If not, maybe we can remove it.

Copy link
Contributor Author

@nathanieliov nathanieliov Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! It can be removed. Thanks.

Copy link
Contributor

@jeremy-then jeremy-then left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

logger.debug("[{}] Rejected peg-in, reason {}", METHOD_NAME, rejectedPeginReason);
eventLogger.logRejectedPegin(btcTx, rejectedPeginReason);

switch (peginProcessAction) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use here the new switch expression with arrow labels

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Comment on lines 513 to 523
// If the peg-in cannot be registered means it should be rejected
Optional<RejectedPeginReason> rejectedPeginReasonOptional = peginEvaluationResult.getRejectedPeginReason();
if (rejectedPeginReasonOptional.isEmpty()) {
// This flow should never be reached. There should always be a rejected pegin reason.
String message = "Invalid state. No rejected reason was returned for an invalid pegin.";
logger.error("[{}] {}", METHOD_NAME, message);
throw new IllegalStateException(message);
}
RejectedPeginReason rejectedPeginReason = rejectedPeginReasonOptional.get();
logger.debug("[{}] Rejected peg-in, reason {}", METHOD_NAME, rejectedPeginReason);
eventLogger.logRejectedPegin(btcTx, rejectedPeginReason);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be made more functional since we are using the Optional API as:

reason = peginEvaluationResult.getRejectedPeginReason()
    .map(rejectedPeginReason -> {
        logger.debug("[{}] Rejected peg-in, reason {}", METHOD_NAME, rejectedPeginReason);
        eventLogger.logRejectedPegin(btcTx, rejectedPeginReason);
        return rejectedPeginReason; // Placeholder if further processing is needed
    })
    .orElseThrow(() -> {
        String message = "Invalid state. No rejected reason was returned for an invalid pegin.";
        logger.error("[{}] {}", METHOD_NAME, message);
        return new IllegalStateException(message);
    });

Also would it make sense to abstract this logic to a private method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just this part regarding getting the rejected pegin reason? If so, to me looks clear how it is now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@@ -1543,12 +1555,11 @@ void pegin_legacy_from_segwit_to_active_fed_cannot_be_processed(
);

// assert

// SINCE RSKIP379 ONLY TRANSACTIONS THAT REALLY ARE PROCESSED, REFUNDS OR REGISTER WILL BE MARK AS PROCESSED.
if (activations == fingerrootActivations){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (activations == fingerrootActivations){
if (activations == fingerrootActivations) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

btcHeightWhenPegoutTxIndexActivates + pegoutTxIndexGracePeriodInBtcBlocks;
}

private PartialMerkleTree createPmtAndMockBlockStore(BtcTransaction btcTransaction)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't there a method in utils already for this? Maybe I am wrong

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend to remove this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this method is needed to recreate a valid chain for the given tx to satisfy the checks in registerBtcTransaction method

Comment on lines 993 to 996
This is a hypothetical case, which is not realistic with the implementation of the svp.
A btc tx hash that is not saved as a spend tx, is identified as a pegin, and will be
rejected due to invalid amount. This is because the pegin amount is below the minimum.
Therefore, this tx should be rejected as pegin and mark as processed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is a hypothetical case, which is not realistic with the implementation of the svp.
A btc tx hash that is not saved as a spend tx, is identified as a pegin, and will be
rejected due to invalid amount. This is because the pegin amount is below the minimum.
Therefore, this tx should be rejected as pegin and mark as processed
This is a hypothetical case, which is not realistic with the implementation of the svp.
A btc tx hash that is not saved as a spend tx is identified as a pegin and will be
rejected due to invalid amount, since the pegin amount is below the minimum.
Therefore, this tx should be rejected as pegin and mark as processed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

- Make logic handling rejected pegin functional using the Optional API
- Fix formatting
Copy link
Contributor

@apancorb apancorb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

private void handleUnprocessableBtcTx(
private RejectedPeginReason handleRejectedPegin(BtcTransaction btcTx,
PeginEvaluationResult peginEvaluationResult) {
return peginEvaluationResult.getRejectedPeginReason().map(reason -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's a good idea to return the rejected peg-in reason here. The method is called handle so I would expect it to do something but not to return anything.

The rejected reason is obtained from PeginEvaluationResult that is received by param. So it's basically returning something that was passed by parameter. Why can't the caller simply get that info from the object?

Maybe this method should renamed to logRejectedPegin? That's what it's doing right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed and rearrange

});
}

private void handleNonRefundablePegin(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the same renaming idea could apply here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -3,5 +3,5 @@
public enum PeginProcessAction {
CAN_BE_REGISTERED,
CAN_BE_REFUNDED,
CANNOT_BE_PROCESSED
CANNOT_BE_REFUNDED
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea, to rename this for clarity

REGISTER,
REFUND,
NO_REFUND

What do you think? A bit more straightforward. Last one could also be IGNORE perhaps, not sure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks simpler. I'll update it.

private static final NetworkParameters btcMainnetParams = bridgeMainnetConstants.getBtcParams();
private static final ActivationConfig.ForBlock arrowHeadActivations = ActivationConfigsForTest.arrowhead600()
.forBlock(0);
private static final ActivationConfig.ForBlock lovellActivations = ActivationConfigsForTest.lovell700()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use all activations? To make sure this keeps working in future network upgrades

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

btcHeightWhenPegoutTxIndexActivates + pegoutTxIndexGracePeriodInBtcBlocks;
}

private PartialMerkleTree createPmtAndMockBlockStore(BtcTransaction btcTransaction)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend to remove this method?

@@ -1,13 +1,13 @@
package co.rsk.peg.utils;

public enum UnrefundablePeginReason {
public enum NoRefundPeginReason {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public enum NoRefundPeginReason {
public enum NonRefundablePeginReason {

I think that sounds better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed

@@ -212,7 +212,7 @@ public void logRejectedPegin(BtcTransaction btcTx, RejectedPeginReason reason) {
}

@Override
public void logUnrefundablePegin(BtcTransaction btcTx, UnrefundablePeginReason reason) {
public void logNoRefundPegin(BtcTransaction btcTx, NoRefundPeginReason reason) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void logNoRefundPegin(BtcTransaction btcTx, NoRefundPeginReason reason) {
public void logNonRefundablePegin(BtcTransaction btcTx, NoRefundPeginReason reason) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed

logger.debug("[{}] Unprocessable transaction {}.", METHOD_NAME, btcTx.getHash());
handleUnprocessableBtcTx(btcTx, peginInformation.getProtocolVersion(), rejectedPeginReason);
}
case NO_REFUND -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case NO_REFUND -> {
case NO_REFUND ->

See Sonar report

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 512 to 527
peginEvaluationResult.getRejectedPeginReason()
.map(reason -> {
logger.debug("[{}] Rejected peg-in, reason {}", METHOD_NAME, reason);
eventLogger.logRejectedPegin(btcTx, reason);
return reason;
}).orElseThrow(() -> {
// This flow should never be reached. There should always be a rejected pegin reason.
String message = "Invalid state. No rejected reason was returned for an invalid pegin.";
logger.error("[{}] {}", METHOD_NAME, message);
return new IllegalStateException(message);
});

logger.debug("[{}] Refunding to address {} ",
METHOD_NAME, peginInformation.getBtcRefundAddress());
generateRejectionRelease(btcTx, peginInformation.getBtcRefundAddress(), rskTxHash,
totalAmount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked better when all this logic was in its own method, why move it all here? It creates a code smell

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the number of parameters that needs to be passed.

handleRefundablePegin(BtcTransaction btcTx, Keccak256 rskTxHash,
        PeginEvaluationResult peginEvaluationResult, Address btcRefundAddress, Coin totalAmount)

.map(reason -> {
logger.debug("[{}] Rejected peg-in, reason {}", METHOD_NAME, reason);
eventLogger.logRejectedPegin(btcTx, reason);
return reason;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need this return here? Nobody is storing that value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You right. It can be removed.

@nathanieliov nathanieliov force-pushed the mark-invalid-pegin-as-processed branch from 39a23fd to 38f7bf2 Compare January 9, 2025 20:49
- Enhance switch for processing each PeginProcessAction
@nathanieliov nathanieliov force-pushed the mark-invalid-pegin-as-processed branch from 38f7bf2 to bf15222 Compare January 9, 2025 21:01
@marcos-iov marcos-iov merged commit ac0df42 into invalid-pegin-as-processed-integration Jan 10, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants