-
Notifications
You must be signed in to change notification settings - Fork 269
Proposal of refactor for BridgeSupportProcessFundsMigrationTest #3436
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
base: mtmu-integration-2
Are you sure you want to change the base?
Proposal of refactor for BridgeSupportProcessFundsMigrationTest #3436
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
marcos-iov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test setup looks in general. I think we can improve a couple of things but not using mocks and not passing unnecessary values to the different builders
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| feePerKbSupport = mock(FeePerKbSupport.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance we could use a real object and not mock this? Like this
| feePerKbSupport = new FeePerKbSupportImpl(feePerKbConstants, feePerKbStorageProvider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| void setUp() { | ||
| feePerKbSupport = mock(FeePerKbSupport.class); | ||
|
|
||
| bridgeEventLogger = mock(BridgeEventLogger.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this? looks like it's only used to pass it to the BridgeSupportBuilder but that's not really needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| // Arrange | ||
| long newFederationCreationBlockNumber = 5L; | ||
| Federation newFederation = standardMultiSigFederationBuilder | ||
| .withCreationTime(Instant.EPOCH) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to pass this? Does it make any difference? Let's try to kkep it as simple as possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| } | ||
| } | ||
|
|
||
| private List<UTXO> generateRetiringFederationUTXOs(Address retiringFederationAddress, int numberOfUtxos, int valuePerOutput) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually doing 2 things and that's not good. It creates and returns the UTXOs but also sets them in the storage which is actually a hidden side effect of the method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| return block; | ||
| } | ||
|
|
||
| private void callUpdateCollections(BridgeSupport bridgeSupport) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider having this in a util class. Not the actual call, but the creation of the updateCollections transaction probably.
Could be in BridgeSupportTestUtil I guess, or feel free to propose another idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| bridgeSupport.updateCollections(updateCollectionsTx); | ||
| } | ||
|
|
||
| private static List<UTXO> getOldFederationUtxos(int numberOfUtxos, int valuePerOutput, Address oldFederationAddress) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just creates UTXOs, doesn't matter if it's for a old/new or even for a federation.
Probably best to have a util to create UTXOs, I think there is already one at least in RskTestUtils class. Or maybe we should have a UTXOBuilder class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In BitcoinTestUtils there is a createUTXOs method. However, it doesn't receive the amount as a parameter. I can create another method in BitcoinTestUtils that also receives the amount. I'm not sure a builder is necessary in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created another public static List<UTXO> createUTXOs(int numberOfUtxos, Coin value, Address receiver) method in BitcionTestUtils
| return oldFederationUtxos; | ||
| } | ||
|
|
||
| public static Repository createRepository() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we already have this in some test utils classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it is repeated in 3 different classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(added to the document)
| } | ||
|
|
||
| private Block getBlockInMigrationAge(long newFederationCreationBlockNumber) { | ||
| Block block = mock(Block.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a method createRskBlock in RskTestUtil, we could use that to avoid creating a mock here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| // TODO(juli): // provider.setPegoutTxSigHash(pegoutTxSigHash.get()); <- idem | ||
| } | ||
|
|
||
| private Coin calculateUtxosTotalValue(List<UTXO> retiringFederationUtxos) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you are just summing up the values of a UTXOs list, I wouldn't call the param retiringFederationUtxos. The method could receive any list of UTXOs regarding of where they come from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| assertTheReleaseOutpointValuesAreCorrect(btcTransaction, numberOfUtxos, retiringFederationUtxos); | ||
|
|
||
| // TODO(juli): // provider.setPegoutTxSigHash(pegoutTxSigHash.get()); <- idem | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert that all available UTXOs were included in the migration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When processing a migration request, BridgeSupport calls provider.setPegoutTxSigHash(pegoutTxSigHash.get()). However, the value stays in the pegoutTxSigHashes set, which is a private variable, until save() is called, which moves the values in the pegoutTxSigHashes to the Repository. Is this out of the scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a way to check all the retiring federation utxos are included in the migration tx
5f6d1f0 to
137059b
Compare
…ectionTransaction to BridgeSupportTestUtil, deleted unnecessary variables in the BridgeSupportProcessFundsMigrationTest setup, created in BitcoinTestUtils a new method to create a list of UTXOs with a certain amount and receiver, used createRskBlock from RskTestUtil
137059b to
9bada07
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors the BridgeSupportProcessFundsMigrationTest test class by extracting common test utilities and consolidating duplicate code, improving test maintainability and reducing code duplication.
Changes:
- Extracted common transaction building and UTXO creation utilities into shared helper classes
- Added a new comprehensive test case for funds migration with proper setup
- Removed duplicate utility methods across multiple test files
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| BridgeSupportProcessFundsMigrationTest.java | Refactored test class with extracted utilities, added new test case, and included setup method with instance variables |
| BridgeSupportTestUtil.java | Added buildUpdateCollectionsTx utility method for transaction creation |
| BitcoinTestUtils.java | Added overloaded createUTXOs method accepting value and receiver parameters |
| BridgeSupportIT.java | Removed duplicate createRepository method, now using shared utility |
| FederationChangeIT.java | Updated to use shared buildUpdateCollectionsTx utility instead of local implementation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| if (activations.isActive(ConsensusRule.RSKIP146)) { | ||
| if (activations.isActive(ConsensusRule.RSKIP146)) { // Papyrus |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While 'Papyrus' appears to be a codename for RSKIP146, this comment lacks context for readers unfamiliar with the codebase. Consider expanding it to explain what Papyrus represents or the significance of this activation rule.
| if (activations.isActive(ConsensusRule.RSKIP146)) { // Papyrus | |
| if (activations.isActive(ConsensusRule.RSKIP146)) { // Papyrus (RSKIP146) network upgrade: enables logging and tracking of migrated pegouts after activation |
| ); | ||
|
|
||
| if (activations.isActive(ConsensusRule.RSKIP376)){ | ||
| if (activations.isActive(ConsensusRule.RSKIP376)){ // Arrowhead |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While 'Arrowhead' appears to be a codename for RSKIP376, this comment lacks context for readers unfamiliar with the codebase. Consider expanding it to explain what Arrowhead represents or the significance of this activation rule.
| if (activations.isActive(ConsensusRule.RSKIP376)){ // Arrowhead | |
| if (activations.isActive(ConsensusRule.RSKIP376)){ // Arrowhead (RSKIP376 upgrade): after activation, pegout BTC transactions must use version 2 instead of the legacy version |
|
|
||
| assertTheReleaseOutpointValuesAreCorrect(btcTransaction, numberOfUtxos, retiringFederationUtxos); | ||
|
|
||
| // TODO(juli): // provider.setPegoutTxSigHash(pegoutTxSigHash.get()); <- idem |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This TODO comment contains unclear references ('juli', 'idem'). TODO comments should clearly describe what needs to be done, why it's needed, and provide enough context for any developer to understand and implement the task.
| // TODO(juli): // provider.setPegoutTxSigHash(pegoutTxSigHash.get()); <- idem | |
| // TODO: When pegout transaction sighash is stored for migrated funds, update this test to set the sighash on the provider and assert it is persisted correctly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…at all the retiringFederation utxos are actually included as input in the migration tx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
rskj-core/src/test/java/co/rsk/peg/BridgeSupportProcessFundsMigrationTest.java
Show resolved
Hide resolved
|
| } | ||
|
|
||
| @Test | ||
| void processFundsMigration_withRetiringGenesisFederationAndNewMultisigFederation_inMigrationAge_shouldMigrateFunds() throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we should not use all activations for testing this scenario. For example, the releaseOutpointsValues assertion does not make sense in this case
| return new MutableRepository(new MutableTrieCache(new MutableTrieImpl(null, new Trie()))); | ||
| } | ||
|
|
||
| public static Transaction buildUpdateCollectionsTx(byte chainId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably not the best to receive a byte as argument, can't we pass the Constants and get the chainId from them?
| var rskTx = CallTransaction.createCallTransaction(nonce, gasPrice.longValue(), | ||
| gasLimit.longValue(), PrecompiledContracts.BRIDGE_ADDR, value, | ||
| Bridge.UPDATE_COLLECTIONS, chainId); | ||
| var randomKey = BtcECKey.fromPrivate(Hex.decode("45c5b07fc1a6f58892615b7c31dca6c96db58c4bbc538a6b8a22999aaa860c32")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where does this priv key came from? can we use a random seed instead?
| Federation retiringFederation = FederationTestUtils.getGenesisFederation(federationConstants); | ||
| int numberOfUtxos = 1; | ||
| Coin valuePerOutput = Coin.valueOf(1_500_000); | ||
| Address retiringFederationAddress = retiringFederation.getAddress(); | ||
|
|
||
| List<UTXO> retiringFederationUtxos = createUTXOs(numberOfUtxos, valuePerOutput, retiringFederationAddress); | ||
| byte[] serializeUTXOList = BridgeSerializationUtils.serializeUTXOList(retiringFederationUtxos); | ||
| bridgeStorageAccessor.saveToRepository(OLD_FEDERATION_BTC_UTXOS_KEY.getKey(), serializeUTXOList); | ||
|
|
||
| long newFederationCreationBlockNumber = 5L; | ||
| Federation newFederation = standardMultiSigFederationBuilder | ||
| .withCreationBlockNumber(newFederationCreationBlockNumber) | ||
| .withNetworkParameters(networkParams) | ||
| .build(); | ||
|
|
||
| federationStorageProvider.setOldFederation(retiringFederation); | ||
| federationStorageProvider.setNewFederation(newFederation); | ||
|
|
||
| Block rskCurrentBlock = getBlockInMigrationAge(newFederationCreationBlockNumber); | ||
| FederationSupport federationSupport = FederationSupportBuilder.builder() | ||
| .withFederationConstants(federationConstants) | ||
| .withFederationStorageProvider(federationStorageProvider) | ||
| .withRskExecutionBlock(rskCurrentBlock) | ||
| .withActivations(activations) | ||
| .build(); | ||
|
|
||
| BridgeSupport bridgeSupport = BridgeSupportBuilder.builder() | ||
| .withBridgeConstants(bridgeMainnetConstants) | ||
| .withProvider(bridgeStorageProvider) | ||
| .withExecutionBlock(rskCurrentBlock) | ||
| .withActivations(activations) | ||
| .withFeePerKbSupport(feePerKbSupport) | ||
| .withFederationSupport(federationSupport) | ||
| .build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed yet, but we should probably move all this to a setup function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totally agree
| } | ||
| } | ||
|
|
||
| private Block getBlockInMigrationAge(long newFederationCreationBlockNumber) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we move this to a utility file as a "getBlockWithHeight" method?



Description
Motivation and Context
How Has This Been Tested?
Types of changes
Checklist: