-
Notifications
You must be signed in to change notification settings - Fork 25
Improve and refactor method to search for stored block in mainchain #574
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: powpeg-refactors-integration
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -385,9 +385,6 @@ | |
| } | ||
|
|
||
| protected int updateBridgeBtcBlockchain() throws BlockStoreException, IOException { | ||
| long bestBlockNumber = federatorSupport.getRskBestChainHeight(); | ||
| boolean useBlockDepth = activationConfig.isActive(ConsensusRule.RSKIP89, bestBlockNumber); | ||
|
|
||
| int bridgeBtcBlockchainBestChainHeight = federatorSupport.getBtcBlockchainBestChainHeight(); | ||
| int federatorBtcBlockchainBestChainHeight = bitcoinWrapper.getBestChainHeight(); | ||
| if (federatorBtcBlockchainBestChainHeight > bridgeBtcBlockchainBestChainHeight) { | ||
|
|
@@ -400,16 +397,12 @@ | |
| // update the bridge with the latest. | ||
|
|
||
| // First, find the common ancestor that is in the federator's bestchain | ||
| // using either the old method -- block locator | ||
| // or the new one -- block depth incremental search | ||
| StoredBlock commonAncestor; | ||
| if (useBlockDepth) { | ||
| commonAncestor = findBridgeBtcBlockchainMatchingAncestor(bridgeBtcBlockchainBestChainHeight); | ||
| } else { | ||
| commonAncestor = findBridgeBtcBlockchainMatchingAncestorUsingBlockLocator(); | ||
| // using block depth incremental search | ||
| Optional<StoredBlock> commonAncestorOpt = findBridgeBtcBlockchainMatchingAncestor(bridgeBtcBlockchainBestChainHeight); | ||
| if (commonAncestorOpt.isEmpty()) { | ||
| throw new BlockStoreException("No best chain block found"); | ||
| } | ||
|
|
||
| checkNotNull(commonAncestor, "No best chain block found"); | ||
| StoredBlock commonAncestor = commonAncestorOpt.get(); | ||
|
|
||
| logger.debug( | ||
| "[updateBridgeBtcBlockchain] Matched block {}.", | ||
|
|
@@ -482,7 +475,7 @@ | |
| } | ||
| } | ||
|
|
||
| private StoredBlock findBridgeBtcBlockchainMatchingAncestor(int bridgeBtcBlockchainBestChainHeight) throws BlockStoreException { | ||
| private Optional<StoredBlock> findBridgeBtcBlockchainMatchingAncestor(int bridgeBtcBlockchainBestChainHeight) throws BlockStoreException { | ||
| // Find the last federator's best chain block the bridge has and update from there | ||
| int bridgeBtcBlockchainInitialBlockHeight = federatorSupport.getBtcBlockchainInitialBlockHeight(); | ||
| int maxSearchDepth = bridgeBtcBlockchainBestChainHeight - bridgeBtcBlockchainInitialBlockHeight; | ||
|
|
@@ -492,27 +485,17 @@ | |
| maxSearchDepth | ||
| ); | ||
|
|
||
| StoredBlock matchedBlock = null; | ||
| int currentSearchDepth = 0; | ||
| int iteration = 0; | ||
| while (true) { | ||
| Sha256Hash storedBlockHash = federatorSupport.getBtcBlockchainBlockHashAtDepth(currentSearchDepth); | ||
| StoredBlock storedBlock = bitcoinWrapper.getBlock(storedBlockHash); | ||
| Optional<StoredBlock> storedBlock = getMatchingStoredBlockInMainChain(storedBlockHash); | ||
| logger.trace( | ||
| "[findBridgeBtcBlockchainMatchingAncestor] block[storedBlockHash] found? {}", | ||
| storedBlock != null | ||
| storedBlock.isPresent() | ||
| ); | ||
| if (storedBlock != null) { | ||
| StoredBlock storedBlockInBestChain = bitcoinWrapper.getBlockAtHeight(storedBlock.getHeight()); | ||
| logger.trace( | ||
| "[findBridgeBtcBlockchainMatchingAncestor] block[{}] in best chain? {}", | ||
| storedBlock.getHeader().getHash(), | ||
| storedBlock.equals(storedBlockInBestChain) | ||
| ); | ||
| if (storedBlock.equals(storedBlockInBestChain)) { | ||
| matchedBlock = storedBlockInBestChain; | ||
| break; | ||
| } | ||
| if (storedBlock.isPresent()) { | ||
| return storedBlock; | ||
| } | ||
|
|
||
| // Have we just searched at maximum depth? If so, no more to search, nothing found! | ||
|
|
@@ -528,36 +511,27 @@ | |
| currentSearchDepth = IntStream.of(1 << iteration, maxSearchDepth).min().getAsInt(); | ||
| iteration++; | ||
| } | ||
|
|
||
| return matchedBlock; | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| private StoredBlock findBridgeBtcBlockchainMatchingAncestorUsingBlockLocator() throws BlockStoreException { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed since only being used pre rskip89 |
||
| // Find the last best chain block the bridge has with respect | ||
| // to the federate node's best chain. | ||
| Object[] blockLocatorArray = federatorSupport.getBtcBlockchainBlockLocator(); | ||
| logger.debug( | ||
| "Block locator size {}, first {}, last {}.", | ||
| blockLocatorArray.length, | ||
| blockLocatorArray[0], | ||
| blockLocatorArray[blockLocatorArray.length - 1] | ||
| private Optional<StoredBlock> getMatchingStoredBlockInMainChain(Sha256Hash blockHash) throws BlockStoreException { | ||
| StoredBlock storedBlock = bitcoinWrapper.getBlock(blockHash); | ||
| if (storedBlock == null) { | ||
| return Optional.empty(); | ||
| } | ||
| int storedBlockHeight = storedBlock.getHeight(); | ||
| StoredBlock storedBlockAtHeightInBestChain = bitcoinWrapper.getBlockAtHeight(storedBlockHeight); | ||
| boolean isBlockInBestChain = storedBlock.equals(storedBlockAtHeightInBestChain); | ||
| logger.trace( | ||
| "[getMatchingStoredBlockInMainChain] block[{}] in best chain? {}", | ||
| storedBlock.getHeader().getHash(), | ||
| isBlockInBestChain | ||
| ); | ||
|
|
||
| StoredBlock matchedBlock = null; | ||
| for (Object o : blockLocatorArray) { | ||
| String blockHash = (String) o; | ||
| StoredBlock storedBlock = bitcoinWrapper.getBlock(Sha256Hash.wrap(blockHash)); | ||
| if (storedBlock == null) { | ||
| continue; | ||
| } | ||
| StoredBlock storedBlockInBestChain = bitcoinWrapper.getBlockAtHeight(storedBlock.getHeight()); | ||
| if (storedBlock.equals(storedBlockInBestChain)) { | ||
| matchedBlock = storedBlockInBestChain; | ||
| break; | ||
| } | ||
| if (isBlockInBestChain) { | ||
| return Optional.of(storedBlockAtHeightInBestChain); | ||
| } | ||
|
|
||
| return matchedBlock; | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| protected void updateBridgeBtcTransactions() { | ||
|
|
@@ -579,7 +553,7 @@ | |
| int numberOfTxsSent = 0; | ||
|
|
||
| Iterator<Sha256Hash> txHashIterator = txsToSendToRskHashes.iterator(); | ||
| while (txHashIterator.hasNext()) { | ||
|
Check warning on line 556 in src/main/java/co/rsk/federate/BtcToRskClient.java
|
||
| Sha256Hash txHash = txHashIterator.next(); | ||
| try { | ||
| Transaction tx = federatorWalletTxMap.get(txHash); | ||
|
|
@@ -603,7 +577,7 @@ | |
| ); | ||
|
|
||
| // Check if the tx was processed (using the tx hash without witness) | ||
| if (federatorSupport.isBtcTxHashAlreadyProcessed(txId)) { | ||
|
Check warning on line 580 in src/main/java/co/rsk/federate/BtcToRskClient.java
|
||
| logger.debug( | ||
| "[updateBridgeBtcTransactions] Btc Tx {} (wtxid: {}) already processed", | ||
| txId, | ||
|
|
@@ -616,7 +590,7 @@ | |
| // If M >= N + K, then remove the transaction from the list | ||
| Long txProcessedHeight = federatorSupport.getBtcTxHashProcessedHeight(txId); | ||
| Long bestChainHeight = federatorSupport.getRskBestChainHeight(); | ||
| if (bestChainHeight >= txProcessedHeight + BTC_TO_RSK_MINIMUM_ACCEPTABLE_CONFIRMATIONS_ON_RSK) { | ||
|
Check failure on line 593 in src/main/java/co/rsk/federate/BtcToRskClient.java
|
||
| removeTxHashFromFile(txHashIterator); | ||
| logger.debug( | ||
| "[updateBridgeBtcTransactions] Btc Tx {} was processed at height {}, current height is {}. Tx removed from pending lock list", | ||
|
|
@@ -704,8 +678,8 @@ | |
|
|
||
| private Optional<StoredBlock> getStoredBlock(Transaction tx) { | ||
| try { | ||
| return Optional.of(findBestChainStoredBlockFor(tx)); | ||
| return findBestChainStoredBlockFor(tx); | ||
| } catch (BlockStoreException | IllegalStateException e) { | ||
|
Check warning on line 682 in src/main/java/co/rsk/federate/BtcToRskClient.java
|
||
| return Optional.empty(); | ||
| } | ||
| } | ||
|
|
@@ -832,24 +806,20 @@ | |
|
|
||
| /** | ||
| * Finds the block in the best chain where supplied tx appears. | ||
| * @throws IllegalStateException If the tx is not in the best chain | ||
| */ | ||
| private StoredBlock findBestChainStoredBlockFor(Transaction tx) throws IllegalStateException, BlockStoreException { | ||
| private Optional<StoredBlock> findBestChainStoredBlockFor(Transaction tx) throws IllegalStateException, BlockStoreException { | ||
| Map<Sha256Hash, Integer> blockHashes = tx.getAppearsInHashes(); | ||
|
|
||
| if (blockHashes != null) { | ||
| for (Sha256Hash blockHash : blockHashes.keySet()) { | ||
| StoredBlock storedBlock = bitcoinWrapper.getBlock(blockHash); | ||
| // Find out if that block is in the main chain | ||
| int height = storedBlock.getHeight(); | ||
| StoredBlock storedBlockAtHeight = bitcoinWrapper.getBlockAtHeight(height); | ||
| if (storedBlockAtHeight!=null && storedBlockAtHeight.getHeader().getHash().equals(blockHash)) { | ||
| return storedBlockAtHeight; | ||
| Optional<StoredBlock> storedBlock = getMatchingStoredBlockInMainChain(blockHash); | ||
| if (storedBlock.isPresent()) { | ||
| return storedBlock; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| throw new IllegalStateException("Tx not in the best chain: " + tx.getWTxId()); | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| @PreDestroy | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -685,149 +685,6 @@ | |
| verify(btcToRskClientFileStorageMock, times(1)).write(any()); | ||
| } | ||
|
|
||
| @Test | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed all pre-rskip89 tests |
||
| void updateBlockchainWithoutBlocks_preRskip89() throws Exception { | ||
| simulatePreRskip89(); | ||
|
|
||
| BitcoinWrapper bw = new SimpleBitcoinWrapper(); | ||
| SimpleFederatorSupport fh = new SimpleFederatorSupport(); | ||
| BtcToRskClient client = createClientWithMocks(bw, fh); | ||
|
|
||
| int numberOfBlocksSent = client.updateBridgeBtcBlockchain(); | ||
| assertEquals(0, numberOfBlocksSent); | ||
|
|
||
| assertNull(fh.getReceiveHeaders()); | ||
| } | ||
|
|
||
| @Test | ||
| void updateBlockchainWithBetterBlockchainInContract_preRskip89() throws Exception { | ||
| simulatePreRskip89(); | ||
|
|
||
| BitcoinWrapper bw = new SimpleBitcoinWrapper(); | ||
| SimpleFederatorSupport fh = new SimpleFederatorSupport(); | ||
| fh.setBtcBlockchainBestChainHeight(10); | ||
| BtcToRskClient client = createClientWithMocks(bw, fh); | ||
|
|
||
| int numberOfBlocksSent = client.updateBridgeBtcBlockchain(); | ||
| assertEquals(0, numberOfBlocksSent); | ||
|
|
||
| assertNull(fh.getReceiveHeaders()); | ||
|
|
||
| assertEquals(0, fh.getSendReceiveHeadersInvocations()); | ||
| } | ||
|
|
||
| @Test | ||
| void updateBlockchainWithBetterBlockchainInWalletByOneBlock_preRskip89() throws Exception { | ||
| simulatePreRskip89(); | ||
|
|
||
| SimpleBitcoinWrapper bw = new SimpleBitcoinWrapper(); | ||
| StoredBlock[] blocks = createBlockchain(3); | ||
| bw.setBlocks(blocks); | ||
| SimpleFederatorSupport fh = new SimpleFederatorSupport(); | ||
| fh.setBtcBlockchainBestChainHeight(2); | ||
| fh.setBtcBlockchainBlockLocator(createLocator(blocks, 2, 0)); | ||
| BtcToRskClient client = createClientWithMocks(bw, fh); | ||
|
|
||
| int numberOfBlocksSent = client.updateBridgeBtcBlockchain(); | ||
|
|
||
| Block[] headers = fh.getReceiveHeaders(); | ||
|
|
||
| assertNotNull(headers); | ||
| assertEquals(1, headers.length); | ||
| assertEquals(1, numberOfBlocksSent); | ||
| assertEquals(blocks[3].getHeader().getHash(), headers[0].getHash()); | ||
|
|
||
| assertEquals(1, fh.getSendReceiveHeadersInvocations()); | ||
| } | ||
|
|
||
| @Test | ||
| void updateBlockchainWithBetterBlockchainInWalletByTwoBlocks_preRskip89() throws Exception { | ||
| simulatePreRskip89(); | ||
|
|
||
| SimpleBitcoinWrapper bw = new SimpleBitcoinWrapper(); | ||
| StoredBlock[] blocks = createBlockchain(4); | ||
| bw.setBlocks(blocks); | ||
| SimpleFederatorSupport fh = new SimpleFederatorSupport(); | ||
| fh.setBtcBlockchainBestChainHeight(2); | ||
| fh.setBtcBlockchainBlockLocator(createLocator(blocks, 2, 0)); | ||
| BtcToRskClient client = createClientWithMocks(bw, fh); | ||
|
|
||
| int numberOfBlocksSent = client.updateBridgeBtcBlockchain(); | ||
|
|
||
| Block[] headers = fh.getReceiveHeaders(); | ||
|
|
||
| assertNotNull(headers); | ||
| assertEquals(2, headers.length); | ||
| assertEquals(2, numberOfBlocksSent); | ||
| assertEquals(blocks[3].getHeader().getHash(), headers[0].getHash()); | ||
| assertEquals(blocks[4].getHeader().getHash(), headers[1].getHash()); | ||
|
|
||
| assertEquals(1, fh.getSendReceiveHeadersInvocations()); | ||
| } | ||
|
|
||
| @Test | ||
| void updateBlockchainWithBetterBlockchainInWalletByThreeBlocks_preRskip89() throws Exception { | ||
| simulatePreRskip89(); | ||
|
|
||
| SimpleBitcoinWrapper bw = new SimpleBitcoinWrapper(); | ||
| StoredBlock[] blocks = createBlockchain(4); | ||
| bw.setBlocks(blocks); | ||
| SimpleFederatorSupport fh = new SimpleFederatorSupport(); | ||
| fh.setBtcBlockchainBestChainHeight(2); | ||
| fh.setBtcBlockchainBlockLocator(createLocator(blocks, 1, 1)); | ||
| BtcToRskClient client = createClientWithMocks(bw, fh); | ||
|
|
||
| client.updateBridgeBtcBlockchain(); | ||
|
|
||
| Block[] headers = fh.getReceiveHeaders(); | ||
|
|
||
| assertNotNull(headers); | ||
| assertEquals(3, headers.length); | ||
| assertEquals(blocks[2].getHeader().getHash(), headers[0].getHash()); | ||
| assertEquals(blocks[3].getHeader().getHash(), headers[1].getHash()); | ||
| assertEquals(blocks[4].getHeader().getHash(), headers[2].getHash()); | ||
|
|
||
| assertEquals(1, fh.getSendReceiveHeadersInvocations()); | ||
| } | ||
|
|
||
| @Test | ||
| void updateBlockchainWithBetterBlockchainInWalletBySixHundredBlocks_preRskip89() throws Exception { | ||
| simulatePreRskip89(); | ||
|
|
||
| StoredBlock[] blocks = createBlockchain(601); | ||
|
|
||
| SimpleBitcoinWrapper bitcoinWrapper = new SimpleBitcoinWrapper(); | ||
| bitcoinWrapper.setBlocks(blocks); | ||
|
|
||
| federatorSupport.setBtcBlockchainBestChainHeight(1); | ||
| federatorSupport.setBtcBlockchainBlockLocator(createLocator(blocks, 1, 0)); | ||
|
|
||
| BtcLockSenderProvider btcLockSenderProvider = mockBtcLockSenderProvider(TxSenderAddressType.P2PKH); | ||
| int amountOfHeadersToSend = 345; | ||
|
|
||
| PowpegNodeSystemProperties config = mock(PowpegNodeSystemProperties.class); | ||
| when(config.getAmountOfHeadersToSend()).thenReturn(amountOfHeadersToSend); | ||
|
|
||
| BtcToRskClient client = btcToRskClientBuilder | ||
| .withActivationConfig(activationConfig) | ||
| .withBitcoinWrapper(bitcoinWrapper) | ||
| .withFederatorSupport(federatorSupport) | ||
| .withBridgeConstants(bridgeRegTestConstants) | ||
| .withBtcLockSenderProvider(btcLockSenderProvider) | ||
| .withFederation(activeFederation) | ||
| .withFedNodeSystemProperties(config) | ||
| .build(); | ||
|
|
||
| client.updateBridgeBtcBlockchain(); | ||
|
|
||
| Block[] headers = federatorSupport.getReceiveHeaders(); | ||
|
|
||
| assertNotNull(headers); | ||
| assertEquals(amountOfHeadersToSend, headers.length); | ||
|
|
||
| assertEquals(1, federatorSupport.getSendReceiveHeadersInvocations()); | ||
| } | ||
|
|
||
| @Test | ||
| void updateBlockchainWithoutBlocks() throws Exception { | ||
| BitcoinWrapper bw = new SimpleBitcoinWrapper(); | ||
|
|
@@ -3637,6 +3494,37 @@ | |
| assertWTxIdIsNotInActiveFedClientProofsFile(peginBtcTx); | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("activeFedArgs") | ||
| void updateBridgeBtcTransactions_whenOneAppearanceHashNotInBlockStore_shouldBeInformedWithBestChainProof( | ||
| Federation federation | ||
| ) throws Exception { | ||
| // arrange | ||
| setUpActiveFedClient(federation); | ||
| var peginBtcTx = createTxFromP2pkh(MAINNET_BTC_PARAMS); | ||
| addOutputToFedWithMinimumPeginValue(peginBtcTx, federation.getAddress()); | ||
|
|
||
| setUpTx(activeFedClient, peginBtcTx); | ||
|
|
||
| // inject other appearance hash that is NOT stubbed in the block store, and | ||
| // that iterates first in the natural-ordered treemap | ||
| String hashThatWillAppearFirst = "aaaaa00000aaaaa00000aaaaa00000aaaaa00000aaaaa00000aaaaa00000aaaa"; | ||
| Sha256Hash appearanceHashFromMissingBlock = Sha256Hash.wrap(hashThatWillAppearFirst); | ||
| Transaction peginTx = walletTxs.iterator().next(); | ||
| peginTx.addBlockAppearance(appearanceHashFromMissingBlock, 1); | ||
| // assert the block won't be found | ||
| assertNull(bitcoinWrapper.getBlock(appearanceHashFromMissingBlock)); | ||
| // and that its hash is the first one in the treemap | ||
| Sha256Hash firstAppearanceHash = peginTx.getAppearsInHashes().keySet().iterator().next(); | ||
| assertEquals(appearanceHashFromMissingBlock, firstAppearanceHash); | ||
|
|
||
| // act | ||
| activeFedClient.updateBridgeBtcTransactions(); | ||
|
|
||
| // assert | ||
| assertTxSentToBridgeByActiveFedClient(peginBtcTx); | ||
| } | ||
|
|
||
| private void assertWTxIdIsInActiveFedClientProofsFile(BtcTransaction btcTx) throws IOException { | ||
| Transaction tx = ThinConverter.toOriginalInstance(MAINNET_BTC_PARAMS_STRING, btcTx); | ||
| assertWTxIdIsInProofsFile(MAINNET_PARAMS, btcToRskActiveFedClientFileStorage, tx); | ||
|
|
@@ -3950,7 +3838,7 @@ | |
| @Test | ||
| void updateBridge_whenFederationIsNull_shouldDoNothing() throws Exception { | ||
| // Arrange | ||
| FederatorSupport federatorSupport = mock(FederatorSupport.class); | ||
|
Check warning on line 3841 in src/test/java/co/rsk/federate/BtcToRskClientTest.java
|
||
|
|
||
| BtcToRskClient btcToRskClient = BtcToRskClientBuilder.builder() | ||
| .withBridgeConstants(BridgeMainNetConstants.getInstance()) | ||
|
|
@@ -4400,10 +4288,6 @@ | |
| return tx; | ||
| } | ||
|
|
||
| private void simulatePreRskip89() { | ||
| when(activationConfig.isActive(eq(ConsensusRule.RSKIP89), anyLong())).thenReturn(false); | ||
| } | ||
|
|
||
| private BtcLockSenderProvider mockBtcLockSenderProvider(TxSenderAddressType txSenderAddressType) { | ||
| BtcLockSender btcLockSender = mock(BtcLockSender.class); | ||
| when(btcLockSender.getTxSenderAddressType()).thenReturn(txSenderAddressType); | ||
|
|
||
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.
no need to check for activations