-
Notifications
You must be signed in to change notification settings - Fork 267
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
Legacy peg in happy path #2886
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Manifest Files |
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.
Looks really good overall. Added a bunch of comments about semantics we tend to use for writing unit tests.
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
private final BridgeSupportBuilder bridgeSupportBuilder = BridgeSupportBuilder.builder(); | ||
|
||
@Test | ||
void whenRegisterALegacyBtcTransactionTheBridgeShouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws BlockStoreException, AddressFormatException, IOException, BridgeIllegalArgumentException { |
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 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.
void whenRegisterALegacyBtcTransactionTheBridgeShouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws BlockStoreException, AddressFormatException, IOException, BridgeIllegalArgumentException { | |
void whenRegisteringALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws Exception { |
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.
Surrounding the methods with try/catch and fail the test in case of an exception, is it a valid approach?
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.
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.
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!
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
private final BridgeSupportBuilder bridgeSupportBuilder = BridgeSupportBuilder.builder(); | ||
|
||
@Test | ||
void whenRegisterALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() { |
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.
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
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activationConfig, bridgeConstants.getFederationConstants()); | ||
|
||
BtcECKey btcPublicKey = new BtcECKey(); | ||
Coin btcTransferred = Coin.COIN; |
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.
Let's use a more common/realistic value, like minimumPegin
, wdyt?
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 thought COIN was normally used as regular value
TransactionOutput output = new TransactionOutput(btcRegTestParams, t, Coin.COIN, new BtcECKey().toAddress(btcRegTestParams)); |
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.
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.
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 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
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.
perfecto, changed already!
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
|
||
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); |
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.
Why do not use the default RepositoryBtcBlockStoreWithCache constructor?
rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Outdated
Show resolved
Hide resolved
21f7774
to
a1d3e08
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.
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) { |
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.
Should this be part of BitcoinTestUtils class maybe?
FederationStorageProvider federationStorageProvider = getFederationStorageProvider(track, federation); | ||
FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activations, bridgeConstants.getFederationConstants()); | ||
|
||
BtcECKey btcPublicKey = new BtcECKey(); |
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.
Let's use BitcoinTestUtils class to create keys using a seed, that way the test is deterministic
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!
FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activationConfig, bridgeConstants.getFederationConstants()); | ||
|
||
BtcECKey btcPublicKey = new BtcECKey(); | ||
Coin btcTransferred = Coin.COIN; |
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 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)); |
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.
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
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.
Noted!
|
||
BtcECKey btcPublicKey = new BtcECKey(); | ||
Coin btcTransferred = Coin.COIN; | ||
BtcTransaction bitcoinTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), btcTransferred, btcPublicKey); |
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.
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
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.
Noted!
} | ||
|
||
private static FeePerKbSupport getFeePerKbSupport() { | ||
FeePerKbSupport 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.
Is there any chance we could create the actual object and not a mock?
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 feePerKbSupport; | ||
} | ||
|
||
private static Block getRskExecutionBlock() { |
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.
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
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.
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)); |
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.
Could we also assert that pegin_btc
event was emitted? With the expected values
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 the transaction was marked as processed
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.
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());
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 the assertion of the event
|
||
return btcTx; | ||
} | ||
} |
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.
Remember to leave a white space at the end of each file
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!
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.
Nice job :) . Watch out for the sonar complaints.
import org.ethereum.vm.PrecompiledContracts; | ||
import org.junit.jupiter.api.Test; | ||
import java.util.*; | ||
|
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.
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) { |
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 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); | ||
} | ||
} |
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.
Missing an empty line in this file for git management
import java.util.*; | ||
|
||
|
||
public class RegisterBtcTransactionIT { |
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.
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) { |
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.
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:
public Script of( |
|
* 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
Description
Added RegisterBtcTransactionIT, a new integration tests suit with the test
whenRegisterALegacyBtcTransactionTheBridgeShouldRegisterTheNewUtxoAndTransferTheRbtcBalance
.Motivation and Context
How Has This Been Tested?
Types of changes
Checklist: