diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java index c116f6ed9b..e273991e5a 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java @@ -93,11 +93,7 @@ void setUp() { // rsk execution block long rskExecutionBlockNumber = 1000L; long rskExecutionBlockTimestamp = 10L; - BlockHeader blockHeader = new BlockHeaderBuilder(mock(ActivationConfig.class)) - .setNumber(rskExecutionBlockNumber) - .setTimestamp(rskExecutionBlockTimestamp) - .build(); - rskExecutionBlock = Block.createBlockFromHeader(blockHeader, true); + rskExecutionBlock = getRskExecutionBlock(rskExecutionBlockNumber, rskExecutionBlockTimestamp); Keccak256 rskTxHash = PegTestUtils.createHash3(1); rskTx = mock(Transaction.class); @@ -247,8 +243,8 @@ private void assertLogCommitFederationFailed() { byte[] proposedFederationRedeemScriptSerialized = proposedFederation.getRedeemScript().getProgram(); byte[] encodedData = getEncodedData(commitFederationFailedEvent, proposedFederationRedeemScriptSerialized, rskExecutionBlock.getNumber()); - assertEventWasEmittedWithExpectedTopics(encodedTopics); - assertEventWasEmittedWithExpectedData(encodedData); + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); + assertEventWasEmittedWithExpectedData(encodedData, logs); } private void assertNoSVPValues() { @@ -387,11 +383,11 @@ private void assertJustUpdateCollectionsWasLogged() { CallTransaction.Function updateCollectionsEvent = BridgeEvents.UPDATE_COLLECTIONS.getEvent(); List encodedTopics = getEncodedTopics(updateCollectionsEvent); - assertEventWasEmittedWithExpectedTopics(encodedTopics); + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); String senderData = rskTx.getSender(mock(SignatureCache.class)).toHexString(); byte[] encodedData = getEncodedData(updateCollectionsEvent, senderData); - assertEventWasEmittedWithExpectedData(encodedData); + assertEventWasEmittedWithExpectedData(encodedData, logs); } } @@ -924,8 +920,8 @@ private void assertLogAddSignature(FederationMember federationMember, byte[] rsk BtcECKey federatorBtcPublicKey = federationMember.getBtcPublicKey(); byte[] encodedData = getEncodedData(addSignatureEvent, federatorBtcPublicKey.getPubKey()); - assertEventWasEmittedWithExpectedTopics(encodedTopics); - assertEventWasEmittedWithExpectedData(encodedData); + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); + assertEventWasEmittedWithExpectedData(encodedData, logs); } private void assertLogReleaseBtc(Keccak256 rskTxHash, BtcTransaction btcTx) { @@ -935,8 +931,8 @@ private void assertLogReleaseBtc(Keccak256 rskTxHash, BtcTransaction btcTx) { byte[] btcTxSerialized = btcTx.bitcoinSerialize(); byte[] encodedData = getEncodedData(releaseBtcEvent, btcTxSerialized); - assertEventWasEmittedWithExpectedTopics(encodedTopics); - assertEventWasEmittedWithExpectedData(encodedData); + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); + assertEventWasEmittedWithExpectedData(encodedData, logs); } private void assertFederatorSignedInputs(List inputs, List sigHashes, BtcECKey key) { @@ -1402,8 +1398,8 @@ private void assertLogReleaseRequested(Keccak256 releaseCreationTxHash, Sha256Ha byte[] encodedData = getEncodedData(releaseRequestedEvent, requestedAmount.getValue()); - assertEventWasEmittedWithExpectedTopics(encodedTopics); - assertEventWasEmittedWithExpectedData(encodedData); + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); + assertEventWasEmittedWithExpectedData(encodedData, logs); } private void assertLogPegoutTransactionCreated(BtcTransaction pegoutTransaction) { @@ -1415,30 +1411,7 @@ private void assertLogPegoutTransactionCreated(BtcTransaction pegoutTransaction) byte[] serializedOutpointValues = UtxoUtils.encodeOutpointValues(outpointValues); byte[] encodedData = getEncodedData(pegoutTransactionCreatedEvent, serializedOutpointValues); - assertEventWasEmittedWithExpectedTopics(encodedTopics); - assertEventWasEmittedWithExpectedData(encodedData); - } - - private List getEncodedTopics(CallTransaction.Function bridgeEvent, Object... args) { - byte[][] encodedTopicsInBytes = bridgeEvent.encodeEventTopics(args); - return LogInfo.byteArrayToList(encodedTopicsInBytes); - } - - private byte[] getEncodedData(CallTransaction.Function bridgeEvent, Object... args) { - return bridgeEvent.encodeEventData(args); - } - - private void assertEventWasEmittedWithExpectedTopics(List expectedTopics) { - Optional topicOpt = logs.stream() - .filter(log -> log.getTopics().equals(expectedTopics)) - .findFirst(); - assertTrue(topicOpt.isPresent()); - } - - private void assertEventWasEmittedWithExpectedData(byte[] expectedData) { - Optional data = logs.stream() - .filter(log -> Arrays.equals(log.getData(), expectedData)) - .findFirst(); - assertTrue(data.isPresent()); + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); + assertEventWasEmittedWithExpectedData(encodedData, logs); } } diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestUtil.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestUtil.java index 12f19b9f56..f4c04238db 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestUtil.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestUtil.java @@ -1,5 +1,6 @@ package co.rsk.peg; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.*; import co.rsk.bitcoinj.core.*; @@ -15,12 +16,11 @@ import java.math.BigInteger; import java.util.*; import org.bouncycastle.util.encoders.Hex; -import org.ethereum.config.blockchain.upgrades.ActivationConfig; -import org.ethereum.core.Block; -import org.ethereum.core.BlockHeader; -import org.ethereum.core.BlockHeaderBuilder; -import org.ethereum.core.Repository; +import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.core.*; import org.ethereum.db.MutableRepository; +import org.ethereum.vm.DataWord; +import org.ethereum.vm.LogInfo; public final class BridgeSupportTestUtil { public static Repository createRepository() { @@ -95,13 +95,34 @@ public static FederationStorageProvider createFederationStorageProvider(Reposito return new FederationStorageProviderImpl(bridgeStorageAccessor); } - public static Block getRskExecutionBlock() { - long rskExecutionBlockNumber = 1000L; - long rskExecutionBlockTimestamp = 10L; - BlockHeader blockHeader = new BlockHeaderBuilder(mock(ActivationConfig.class)) + public static Block getRskExecutionBlock(long rskExecutionBlockNumber, long rskExecutionBlockTimestamp) { + BlockHeader blockHeader = new BlockHeaderBuilder(ActivationConfigsForTest.all()) .setNumber(rskExecutionBlockNumber) .setTimestamp(rskExecutionBlockTimestamp) .build(); return Block.createBlockFromHeader(blockHeader, true); } + + public static List getEncodedTopics(CallTransaction.Function bridgeEvent, Object... args) { + byte[][] encodedTopicsInBytes = bridgeEvent.encodeEventTopics(args); + return LogInfo.byteArrayToList(encodedTopicsInBytes); + } + + public static byte[] getEncodedData(CallTransaction.Function bridgeEvent, Object... args) { + return bridgeEvent.encodeEventData(args); + } + + public static void assertEventWasEmittedWithExpectedTopics(List expectedTopics, List logs) { + Optional topicOpt = logs.stream() + .filter(log -> log.getTopics().equals(expectedTopics)) + .findFirst(); + assertTrue(topicOpt.isPresent()); + } + + public static void assertEventWasEmittedWithExpectedData(byte[] expectedData, List logs) { + Optional data = logs.stream() + .filter(log -> Arrays.equals(log.getData(), expectedData)) + .findFirst(); + assertTrue(data.isPresent()); + } } diff --git a/rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java b/rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java index 0bbf9231ec..1f7ed3484c 100644 --- a/rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java +++ b/rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java @@ -2,7 +2,6 @@ import static co.rsk.peg.BridgeSupportTestUtil.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; import co.rsk.bitcoinj.core.*; import co.rsk.bitcoinj.script.ScriptBuilder; @@ -26,131 +25,153 @@ import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.core.*; +import org.ethereum.crypto.ECKey; +import org.ethereum.vm.DataWord; +import org.ethereum.vm.LogInfo; import org.ethereum.vm.PrecompiledContracts; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.*; class RegisterBtcTransactionIT { - + public static final long RSK_EXECUTION_BLOCK_NUMBER = 1000L; + public static final long RSK_EXECUTION_BLOCK_TIMESTAMP = 10L; private final BridgeConstants bridgeConstants = BridgeMainNetConstants.getInstance(); - private final NetworkParameters btcParams = bridgeConstants.getBtcParams(); + private final NetworkParameters btcNetworkParams = bridgeConstants.getBtcParams(); private final BridgeSupportBuilder bridgeSupportBuilder = BridgeSupportBuilder.builder(); - - @Test - void whenRegisterALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws Exception { - // Arrange - ActivationConfig.ForBlock activations = ActivationConfigsForTest.all().forBlock(0); - Repository repository = BridgeSupportTestUtil.createRepository(); - Repository track = repository.startTracking(); - Block rskExecutionBlock = getRskExecutionBlock(); + private final ActivationConfig.ForBlock activations = ActivationConfigsForTest.all().forBlock(0); + private final Transaction rskTx = TransactionUtils.createTransaction(); + private final Coin minimumPeginValue = bridgeConstants.getMinimumPeginTxValue(activations); + private final Block rskExecutionBlock = getRskExecutionBlock(RSK_EXECUTION_BLOCK_NUMBER, RSK_EXECUTION_BLOCK_TIMESTAMP); + private Repository repository; + private FederationSupport federationSupport; + private BridgeStorageProvider bridgeStorageProvider; + private BtcTransaction bitcoinTransaction; + private PartialMerkleTree pmtWithTransactions; + private int btcBlockWithPmtHeight; + private RskAddress rskReceiver; + private BridgeSupport bridgeSupport; + private ArrayList logs; + + + @BeforeEach + void setUp() throws Exception{ + repository = BridgeSupportTestUtil.createRepository().startTracking(); BtcLockSenderProvider btcLockSenderProvider = new BtcLockSenderProvider(); - FeePerKbSupport feePerKbSupport = getFeePerKbSupport(repository, bridgeConstants); + StorageAccessor bridgeStorageAccessor = new BridgeStorageAccessorImpl(repository); + + FeePerKbStorageProvider feePerKbStorageProvider = new FeePerKbStorageProviderImpl(bridgeStorageAccessor); + FeePerKbSupport feePerKbSupport = new FeePerKbSupportImpl(bridgeConstants.getFeePerKbConstants(), feePerKbStorageProvider); + FederationStorageProvider federationStorageProvider = new FederationStorageProviderImpl(bridgeStorageAccessor); Federation federation = P2shErpFederationBuilder.builder().build(); - FederationStorageProvider federationStorageProvider = getFederationStorageProvider(track, federation); - FederationSupport federationSupport = getFederationSupport(federationStorageProvider, activations, bridgeConstants.getFederationConstants()); + federationStorageProvider.setNewFederation(federation); + FederationConstants federationConstants = bridgeConstants.getFederationConstants(); + federationSupport = FederationSupportBuilder.builder() + .withFederationConstants(federationConstants) + .withFederationStorageProvider(federationStorageProvider) + .withActivations(activations) + .build(); - BtcECKey btcPublicKey = BitcoinTestUtils.getBtcEcKeyFromSeed("seed"); - Coin btcTransferred = bridgeConstants.getMinimumPeginTxValue(activations); - BtcTransaction bitcoinTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), btcTransferred, btcPublicKey); - TransactionOutput output = bitcoinTransaction.getOutput(0); - List expectedFederationUtxos = Collections.singletonList(utxoOf(bitcoinTransaction, output)); + bridgeStorageProvider = new BridgeStorageProvider(repository, PrecompiledContracts.BRIDGE_ADDR, btcNetworkParams, activations); + BtcBlockStoreWithCache.Factory btcBlockStoreFactory = new RepositoryBtcBlockStoreWithCache.Factory(btcNetworkParams, 100, 100); + BtcBlockStoreWithCache btcBlockStoreWithCache = btcBlockStoreFactory.newInstance(repository, bridgeConstants, bridgeStorageProvider, activations); - BridgeStorageProvider bridgeStorageProvider = new BridgeStorageProvider(track, PrecompiledContracts.BRIDGE_ADDR, bridgeConstants.getBtcParams(), activations); - BtcBlockStoreWithCache.Factory btcBlockStoreFactory = new RepositoryBtcBlockStoreWithCache.Factory(bridgeConstants.getBtcParams(), 100, 100); - BtcBlockStoreWithCache btcBlockStoreWithCache = btcBlockStoreFactory.newInstance(track, bridgeConstants, bridgeStorageProvider, activations); + BtcECKey btcPublicKey = BitcoinTestUtils.getBtcEcKeyFromSeed("seed"); + ECKey ecKey = ECKey.fromPublicOnly(btcPublicKey.getPubKey()); + rskReceiver = new RskAddress(ecKey.getAddress()); + bitcoinTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey); - PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(Collections.singletonList(bitcoinTransaction.getHash()), bridgeConstants.getBtcParams()); - int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks(); + pmtWithTransactions = createValidPmtForTransactions(List.of(bitcoinTransaction.getHash()), btcNetworkParams); + btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks(); int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations(); - BridgeEventLoggerImpl bridgeEventLogger = spy(new BridgeEventLoggerImpl(bridgeConstants, activations, new ArrayList<>())); - - recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, bridgeConstants.getBtcParams()); + logs = new ArrayList<>(); + BridgeEventLoggerImpl bridgeEventLogger = new BridgeEventLoggerImpl(bridgeConstants, activations, logs); + recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcNetworkParams); bridgeStorageProvider.save(); - BridgeSupport bridgeSupport = getBridgeSupport(bridgeEventLogger, bridgeStorageProvider, activations, federationSupport, feePerKbSupport, rskExecutionBlock, btcBlockStoreFactory, track, btcLockSenderProvider); - - Transaction rskTx = TransactionUtils.createTransaction(); - org.ethereum.crypto.ECKey key = org.ethereum.crypto.ECKey.fromPublicOnly(btcPublicKey.getPubKey()); - - RskAddress receiver = new RskAddress(key.getAddress()); - co.rsk.core.Coin receiverBalance = track.getBalance(receiver); - co.rsk.core.Coin expectedReceiverBalance = receiverBalance.add(co.rsk.core.Coin.fromBitcoin(btcTransferred)); + bridgeSupport = bridgeSupportBuilder + .withBridgeConstants(bridgeConstants) + .withProvider(bridgeStorageProvider) + .withActivations(activations) + .withEventLogger(bridgeEventLogger) + .withFederationSupport(federationSupport) + .withFeePerKbSupport(feePerKbSupport) + .withExecutionBlock(rskExecutionBlock) + .withBtcBlockStoreFactory(btcBlockStoreFactory) + .withRepository(repository) + .withBtcLockSenderProvider(btcLockSenderProvider) + .build(); + } + @Test + void registerBtcTransaction_forALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws Exception { // Act bridgeSupport.registerBtcTransaction(rskTx, bitcoinTransaction.bitcoinSerialize(), btcBlockWithPmtHeight, pmtWithTransactions.bitcoinSerialize()); - bridgeSupport.save(); - track.commit(); // Assert Optional heightIfBtcTxHashIsAlreadyProcessed = bridgeStorageProvider.getHeightIfBtcTxhashIsAlreadyProcessed(bitcoinTransaction.getHash()); - assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent()); - assertEquals(rskExecutionBlock.getNumber(), heightIfBtcTxHashIsAlreadyProcessed.get()); + assertEquals(RSK_EXECUTION_BLOCK_NUMBER, heightIfBtcTxHashIsAlreadyProcessed.get()); + + int outputIndex = 0; + TransactionOutput output = bitcoinTransaction.getOutput(outputIndex); + List expectedFederationUtxos = Collections.singletonList(utxoOf(bitcoinTransaction, output)); assertEquals(expectedFederationUtxos, federationSupport.getActiveFederationBtcUTXOs()); - assertEquals(expectedReceiverBalance, repository.getBalance(receiver)); - verify(bridgeEventLogger, times(1)).logPeginBtc(receiver, bitcoinTransaction, btcTransferred, 0); - } + co.rsk.core.Coin expectedReceiverBalance = co.rsk.core.Coin.fromBitcoin(output.getValue()); + assertEquals(expectedReceiverBalance, repository.getBalance(rskReceiver)); - private static UTXO utxoOf(BtcTransaction bitcoinTransaction, TransactionOutput output) { - return new UTXO( - bitcoinTransaction.getHash(), - output.getIndex(), - output.getValue(), - 0, - bitcoinTransaction.isCoinBase(), - output.getScriptPubKey() - ); + assertLogPegInBtc(); } - private static FederationSupport getFederationSupport(FederationStorageProvider federationStorageProvider, ActivationConfig.ForBlock activationConfig, FederationConstants federationConstants) { - return FederationSupportBuilder.builder() - .withFederationConstants(federationConstants) - .withFederationStorageProvider(federationStorageProvider) - .withActivations(activationConfig) - .build(); - } + @Test + void registerBtcTransaction_forARepeatedLegacyBtcTransaction_shouldNotPerformAnyChange() throws Exception { + // Arrange + bridgeSupport.registerBtcTransaction(rskTx, bitcoinTransaction.bitcoinSerialize(), btcBlockWithPmtHeight, pmtWithTransactions.bitcoinSerialize()); + bridgeSupport.save(); - private FederationStorageProvider getFederationStorageProvider(Repository track, Federation federation) { - FederationStorageProvider federationStorageProvider = createFederationStorageProvider(track); - federationStorageProvider.setNewFederation(federation); - return federationStorageProvider; - } + co.rsk.core.Coin expectedReceiverBalance = repository.getBalance(rskReceiver); + List expectedFederationUTXOs = federationSupport.getActiveFederationBtcUTXOs(); - private static FeePerKbSupport getFeePerKbSupport(Repository repository, BridgeConstants bridgeConstants) { - StorageAccessor bridgeStorageAccessor = new BridgeStorageAccessorImpl(repository); - FeePerKbStorageProvider feePerKbStorageProvider = new FeePerKbStorageProviderImpl(bridgeStorageAccessor); - return new FeePerKbSupportImpl( - bridgeConstants.getFeePerKbConstants(), - feePerKbStorageProvider - ); + // Act + bridgeSupport.registerBtcTransaction(rskTx, bitcoinTransaction.bitcoinSerialize(), btcBlockWithPmtHeight, pmtWithTransactions.bitcoinSerialize()); + bridgeSupport.save(); + + // Assert + assertEquals(expectedFederationUTXOs, federationSupport.getActiveFederationBtcUTXOs()); + assertEquals(expectedReceiverBalance, repository.getBalance(rskReceiver)); } - private BridgeSupport getBridgeSupport(BridgeEventLoggerImpl bridgeEventLogger, BridgeStorageProvider bridgeStorageProvider, ActivationConfig.ForBlock activationsBeforeForks, FederationSupport federationSupport, FeePerKbSupport feePerKbSupport, Block rskExecutionBlock, BtcBlockStoreWithCache.Factory btcBlockStoreFactory, Repository repository, BtcLockSenderProvider btcLockSenderProvider) { - return bridgeSupportBuilder - .withBridgeConstants(bridgeConstants) - .withProvider(bridgeStorageProvider) - .withEventLogger(bridgeEventLogger) - .withActivations(activationsBeforeForks) - .withFederationSupport(federationSupport) - .withFeePerKbSupport(feePerKbSupport) - .withExecutionBlock(rskExecutionBlock) - .withBtcBlockStoreFactory(btcBlockStoreFactory) - .withRepository(repository) - .withBtcLockSenderProvider(btcLockSenderProvider) - .build(); + private static UTXO utxoOf(BtcTransaction bitcoinTransaction, TransactionOutput output) { + return new UTXO( + bitcoinTransaction.getHash(), + output.getIndex(), + output.getValue(), + 0, + bitcoinTransaction.isCoinBase(), + output.getScriptPubKey() + ); } private BtcTransaction createPegInTransaction(Address federationAddress, Coin coin, BtcECKey pubKey) { - BtcTransaction btcTx = new BtcTransaction(btcParams); + BtcTransaction btcTx = new BtcTransaction(btcNetworkParams); btcTx.addInput(BitcoinTestUtils.createHash(0), 0, ScriptBuilder.createInputScript(null, pubKey)); - btcTx.addOutput(new TransactionOutput(btcParams, btcTx, coin, federationAddress)); + btcTx.addOutput(new TransactionOutput(btcNetworkParams, btcTx, coin, federationAddress)); return btcTx; } + + private void assertLogPegInBtc() { + Sha256Hash peginTransactionHash = bitcoinTransaction.getHash(); + List encodedTopics = getEncodedTopics(BridgeEvents.PEGIN_BTC.getEvent(), rskReceiver.toString(), peginTransactionHash.getBytes()); + byte[] encodedData = getEncodedData(BridgeEvents.PEGIN_BTC.getEvent(), minimumPeginValue.getValue(), 0); + + assertEventWasEmittedWithExpectedTopics(encodedTopics, logs); + assertEventWasEmittedWithExpectedData(encodedData, logs); + } }