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

Legacy peg in happy path #2886

Merged
merged 12 commits into from
Dec 16, 2024
Merged

Conversation

julianlen
Copy link
Contributor

Description

Added RegisterBtcTransactionIT, a new integration tests suit with the test whenRegisterALegacyBtcTransactionTheBridgeShouldRegisterTheNewUtxoAndTransferTheRbtcBalance.

Motivation and Context

How Has This Been Tested?

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)
  • Other information:

@julianlen julianlen requested a review from a team as a code owner December 10, 2024 14:14
Copy link

github-actions bot commented Dec 10, 2024

Dependency Review

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

OpenSSF Scorecard

PackageVersionScoreDetails

Scanned Manifest Files

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.

Looks really good overall. Added a bunch of comments about semantics we tend to use for writing unit tests.

private final BridgeSupportBuilder bridgeSupportBuilder = BridgeSupportBuilder.builder();

@Test
void whenRegisterALegacyBtcTransactionTheBridgeShouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws BlockStoreException, AddressFormatException, IOException, BridgeIllegalArgumentException {
Copy link
Contributor

Choose a reason for hiding this comment

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

The pattern we tend to follow is methodName_when..._should.... Since this is an integration test is fine to leave as blank the methodName part.

Note also that there is really not a need to declare all the types of exceptions since this is a test. If a test expects an exception to be thrown it should be asserted. So a simple Exception declaration will suffice.

Suggested change
void whenRegisterALegacyBtcTransactionTheBridgeShouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws BlockStoreException, AddressFormatException, IOException, BridgeIllegalArgumentException {
void whenRegisteringALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws Exception {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Surrounding the methods with try/catch and fail the test in case of an exception, is it a valid approach?

Copy link
Contributor

Choose a reason for hiding this comment

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

What I mean is that if the test expects an exception thrown, then we can assert it as assertThrows(Exception.class, () -> methodThatThrowsException());. But here since it is not expected we can declare it in the method signature throws Exception. This reduces boilerplate code since we know this test will not throw an exception since it is not intended.

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!

@julianlen julianlen requested a review from apancorb December 11, 2024 19:50
private final BridgeSupportBuilder bridgeSupportBuilder = BridgeSupportBuilder.builder();

@Test
void whenRegisterALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() {
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
void whenRegisterALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() {
void whenValidLegacyPegin_shouldRegisterNewUtxoAndTransferTheRbtcBalance() {

We usually use this naming convention "$method_$condition_$expectedResult". I assume, in this case, we don't need the method_name prefix since it is already expressed in the class name, but I think we can keep the rest part as we already do following the existing naming convention used in other tests

FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activationConfig, bridgeConstants.getFederationConstants());

BtcECKey btcPublicKey = new BtcECKey();
Coin btcTransferred = Coin.COIN;
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use a more common/realistic value, like minimumPegin, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought COIN was normally used as regular value

TransactionOutput output = new TransactionOutput(btcRegTestParams, t, Coin.COIN, new BtcECKey().toAddress(btcRegTestParams));

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, on Mainnet, I don't think many users will send 1 BTC. The most common value I think, will be close to the minimum pegin value.

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably a good idea to use the min peg-in value since it makes the test adapt in case that value ever changes. And we are also testing a corner case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

perfecto, changed already!


BridgeStorageProvider bridgeStorageProvider = new BridgeStorageProvider(track, PrecompiledContracts.BRIDGE_ADDR, bridgeConstants.getBtcParams(), activationConfig);
BtcBlockStoreWithCache.Factory btcBlockStoreFactory = new RepositoryBtcBlockStoreWithCache.Factory(bridgeConstants.getBtcParams(), 100, 100);
BtcBlockStoreWithCache btcBlockStoreWithCache = btcBlockStoreFactory.newInstance(track, bridgeConstants, bridgeStorageProvider, activationConfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do not use the default RepositoryBtcBlockStoreWithCache constructor?

@julianlen julianlen force-pushed the pegin-it-initialTest branch from 21f7774 to a1d3e08 Compare December 12, 2024 20:14
Copy link
Contributor

@marcos-iov marcos-iov left a comment

Choose a reason for hiding this comment

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

Looking good, well done. Left some ideas and suggestions

@@ -52,7 +56,7 @@ public static void recreateChainFromPmt(
btcBlockStoreWithCache.setChainHead(storedChainHeadBlock);
}

private static BtcBlock createBtcBlockWithPmt(PartialMerkleTree pmt, NetworkParameters networkParameters) {
public static BtcBlock createBtcBlockWithPmt(PartialMerkleTree pmt, NetworkParameters networkParameters) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be part of BitcoinTestUtils class maybe?

FederationStorageProvider federationStorageProvider = getFederationStorageProvider(track, federation);
FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activations, bridgeConstants.getFederationConstants());

BtcECKey btcPublicKey = new BtcECKey();
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use BitcoinTestUtils class to create keys using a seed, that way the test is deterministic

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!

FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activationConfig, bridgeConstants.getFederationConstants());

BtcECKey btcPublicKey = new BtcECKey();
Coin btcTransferred = Coin.COIN;
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably a good idea to use the min peg-in value since it makes the test adapt in case that value ever changes. And we are also testing a corner case

Coin btcTransferred = Coin.COIN;
BtcTransaction bitcoinTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), btcTransferred, btcPublicKey);
TransactionOutput output = bitcoinTransaction.getOutput(0);
List<UTXO> expectedFederationUtxos = Collections.singletonList(getUtxo(bitcoinTransaction, output));
Copy link
Contributor

Choose a reason for hiding this comment

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

Something very very similar is already done in BridgeSupport::saveNewUTXOs.

Perhaps this should be a util method in BitcoinUtils class? To be used both in tests and in the Bridge code.

Not to do it as part of this PR, but we could create a new ticket for that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted!


BtcECKey btcPublicKey = new BtcECKey();
Coin btcTransferred = Coin.COIN;
BtcTransaction bitcoinTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), btcTransferred, btcPublicKey);
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we could consider creating a PeginTransactionBuilder? What do you think? Would it be worth it?

With some methods like addInput(prevHash, outputIndex) and addOutput(address, value). This could then scale to considering different address types maybe so we could start setting up new test cases easily.

Again, not to apply in this PR but something to consider as part of this work

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted!

}

private static FeePerKbSupport getFeePerKbSupport() {
FeePerKbSupport feePerKbSupport = mock(FeePerKbSupport.class);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any chance we could create the actual object and not a mock?

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!

return feePerKbSupport;
}

private static Block getRskExecutionBlock() {
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 could go in BridgeSupportTestUtil? Seems like it might be useful in other places. Or in RskTestUtil maybe if we plan to use this for other things than just the Bridge

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I'll move it to BridgeSupportTestUtil. If in the future we need it for something but the Bridge, we can move it to RskTestUtil to make it more "general"

assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent());
assertEquals(rskExecutionBlock.getNumber(), heightIfBtcTxHashIsAlreadyProcessed.get());
assertEquals(expectedFederationUtxos, federationSupport.getActiveFederationBtcUTXOs());
assertEquals(expectedReceiverBalance, repository.getBalance(receiver));
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we also assert that pegin_btc event was emitted? With the expected values

Copy link
Contributor

Choose a reason for hiding this comment

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

Assert that the transaction was marked 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.

Regarding the assert whether the tx was marked as processed I have

Optional<Long> heightIfBtcTxHashIsAlreadyProcessed = bridgeStorageProvider.getHeightIfBtcTxhashIsAlreadyProcessed(bitcoinTransaction.getHash());
assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent());
assertEquals(rskExecutionBlock.getNumber(), heightIfBtcTxHashIsAlreadyProcessed.get());

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 the assertion of the event


return btcTx;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Remember to leave a white space at the end of each file

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

@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.

Nice job :) . Watch out for the sonar complaints.

import org.ethereum.vm.PrecompiledContracts;
import org.junit.jupiter.api.Test;
import java.util.*;

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: remove extra blank line

);
}

private BridgeSupport getBridgeSupport(BridgeEventLoggerImpl bridgeEventLogger, BridgeStorageProvider bridgeStorageProvider, ActivationConfig.ForBlock activationsBeforeForks, FederationSupport federationSupport, FeePerKbSupport feePerKbSupport, Block rskExecutionBlock, BtcBlockStoreWithCache.Factory btcBlockStoreFactory, Repository repository, BtcLockSenderProvider btcLockSenderProvider) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In the future as discussed in another PR, lets avoid this type of methods and use the builder directly

private FederationStorageProvider createFederationStorageProvider(Repository repository) {
StorageAccessor bridgeStorageAccessor = new BridgeStorageAccessorImpl(repository);
return new FederationStorageProviderImpl(bridgeStorageAccessor);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing an empty line in this file for git management

import java.util.*;


public class RegisterBtcTransactionIT {
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the public modifier to comply with Sonar rules.

verify(bridgeEventLogger, times(1)).logPeginBtc(receiver, bitcoinTransaction, btcTransferred, 0);
}

private static UTXO getUtxo(BtcTransaction bitcoinTransaction, TransactionOutput output) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of naming this method utxoOf following the Java naming convention used for static creational patterns? See:

List.of
Set.of
LocalDate.of

We've been using this naming convention in some places. For example:

@julianlen julianlen merged commit db3f6bc into pegin-tests-integration Dec 16, 2024
9 checks passed
@julianlen julianlen deleted the pegin-it-initialTest branch December 16, 2024 18:49
julianlen added a commit that referenced this pull request Dec 16, 2024
* feat: first happy path for a peg in

* fix: declaration of utxo

* fix: black space at the end of the file

* fix: assert that the event pegin_btc has been emitted with the correct parameters

* feat: replaced mocked feePerKeb constant for an actual instance

* fix: now the peg in uses the minimum pegin tx value

* fix: moved the method getRskExecutionBlock to BridgeSupoprtTestUtil

* fix: using BitcoinTestUtils to make the btcPublicKey generation deterministic

* fix: erased extra line

* fix: added in BridgeSupportIT an extra line in the end

* fix: removed the public modifier in the RegisterBTCTransactionIT

* fix: renamed getUtxo to utxoOf
@aeidelman aeidelman added this to the Lovell 7.0.0 milestone Jan 28, 2025
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.

5 participants