From d9cab35b1bfef6542bc81ef70d63279b6d0054d5 Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Thu, 21 Nov 2024 23:58:07 -0400 Subject: [PATCH] - Add the use of serializeCompactV2 - Add the use of deserializeCompactV2 --- .../peg/RepositoryBtcBlockStoreWithCache.java | 28 +- .../test/java/co/rsk/peg/BridgeSupportIT.java | 4 +- ...ryBtcBlockStoreWithCacheChainWorkTest.java | 441 ++++++++++++++++++ 3 files changed, 468 insertions(+), 5 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/peg/RepositoryBtcBlockStoreWithCacheChainWorkTest.java diff --git a/rskj-core/src/main/java/co/rsk/peg/RepositoryBtcBlockStoreWithCache.java b/rskj-core/src/main/java/co/rsk/peg/RepositoryBtcBlockStoreWithCache.java index 04db27e4e70..86e80737da1 100644 --- a/rskj-core/src/main/java/co/rsk/peg/RepositoryBtcBlockStoreWithCache.java +++ b/rskj-core/src/main/java/co/rsk/peg/RepositoryBtcBlockStoreWithCache.java @@ -18,6 +18,9 @@ package co.rsk.peg; +import static co.rsk.bitcoinj.core.StoredBlock.deserializeCompactLegacy; +import static co.rsk.bitcoinj.core.StoredBlock.deserializeCompactV2; + import co.rsk.bitcoinj.core.BtcBlock; import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.core.Sha256Hash; @@ -308,17 +311,36 @@ public StoredBlock getStoredBlockAtMainChainDepth(int depth) throws BlockStoreEx } private byte[] storedBlockToByteArray(StoredBlock block) { - ByteBuffer byteBuffer = ByteBuffer.allocate(128); - block.serializeCompact(byteBuffer); + ByteBuffer byteBuffer = serializeBlock(block); byte[] ba = new byte[byteBuffer.position()]; byteBuffer.flip(); byteBuffer.get(ba); return ba; } + private ByteBuffer serializeBlock(StoredBlock block) { + if (shouldUseLegacy12ByteChainworkFormat()) { + ByteBuffer byteBuffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE_LEGACY); + block.serializeCompactLegacy(byteBuffer); + return byteBuffer; + } + + ByteBuffer byteBuffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE_V2); + block.serializeCompactV2(byteBuffer); + return byteBuffer; + } + + private boolean shouldUseLegacy12ByteChainworkFormat() { + return !activations.isActive(ConsensusRule.RSKIP454); + } + private StoredBlock byteArrayToStoredBlock(byte[] ba) { ByteBuffer byteBuffer = ByteBuffer.wrap(ba); - return StoredBlock.deserializeCompact(btcNetworkParams, byteBuffer); + if (ba.length == StoredBlock.COMPACT_SERIALIZED_SIZE_LEGACY) { + return deserializeCompactLegacy(btcNetworkParams, byteBuffer); + } + + return deserializeCompactV2(btcNetworkParams, byteBuffer); } private void checkIfInitialized() { diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportIT.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportIT.java index c78d444191c..c29f595699b 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportIT.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportIT.java @@ -386,10 +386,10 @@ private InputStream getCheckpoints(NetworkParameters networkParameters, List cacheBlocks; + private static final RskAddress BRIDGE_ADDR = PrecompiledContracts.BRIDGE_ADDR; + private BridgeStorageProvider bridgeStorageProvider; + private RepositoryBtcBlockStoreWithCache repositoryBtcBlockStoreWithCache; + + // Just an arbitrary block + private static final String blockHeader = "00e00820925b77c9ff4d0036aa29f3238cde12e9af9d55c34ed30200000000000000000032a9fa3e12ef87a2327b55db6a16a1227bb381db8b269d90aa3a6e38cf39665f91b47766255d0317c1b1575f"; + private static final int blockHeight = 849137; + private static final BtcBlock BLOCK = new BtcBlock(mainneNetworkParameters, Hex.decode(blockHeader)); + + @BeforeEach + void setUp() { + repository = spy(new MutableRepository( + new MutableTrieCache(new MutableTrieImpl(null, new Trie())) + )); + } + + void arrange(ActivationConfig.ForBlock activations) { + bridgeStorageProvider = new BridgeStorageProvider(repository, BRIDGE_ADDR, mainneNetworkParameters, activations); + + repositoryBtcBlockStoreWithCache = new RepositoryBtcBlockStoreWithCache( + mainneNetworkParameters, + repository, + cacheBlocks, + BRIDGE_ADDR, + bridgeMainnetConstants, + bridgeStorageProvider, + activations + ); + } + + @ParameterizedTest() + @MethodSource("invalidChainWorkForV1") + void put_preRskip_whenInvalidChainWorkForV1_shouldFail(BigInteger chainWork) { + arrange(arrowHeadActivations); + StoredBlock storedBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + IllegalArgumentException actualException = Assertions.assertThrows( + IllegalArgumentException.class, () -> { + repositoryBtcBlockStoreWithCache.put(storedBlock); + }); + + String expectedMessage = "The given number does not fit in 12"; + String actualMessage = actualException.getMessage(); + Assertions.assertEquals(expectedMessage, actualMessage); + } + + private static Stream invalidChainWorkForV1() { + return Stream.of( + Arguments.of(TOO_LARGE_WORK_V1), + Arguments.of(MAX_WORK_V2), + Arguments.of(TOO_LARGE_WORK_V2) + ); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void put_preRskip_whenValidChainWorkForV1_shouldStoreBlock(BigInteger chainWork) { + arrange(arrowHeadActivations); + StoredBlock storedBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + repositoryBtcBlockStoreWithCache.put(storedBlock); + + // assert + Sha256Hash expectedHash = storedBlock.getHeader().getHash(); + + int expectedCompactSerializedSize = COMPACT_SERIALIZED_SIZE_LEGACY; + ByteBuffer byteBufferForExpectedBlock = ByteBuffer.allocate(expectedCompactSerializedSize); + storedBlock.serializeCompactLegacy(byteBufferForExpectedBlock); + + byte[] expectedSerializedBlock = byteBufferForExpectedBlock.array(); + verify(repository, times(1)).addStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()), + expectedSerializedBlock + ); + byte[] actualSerializedBlock = repository.getStorageBytes(BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString())); + Assertions.assertNotNull(actualSerializedBlock); + Assertions.assertEquals(expectedCompactSerializedSize, actualSerializedBlock.length); + + Assertions.assertArrayEquals(expectedSerializedBlock, actualSerializedBlock); + } + + private static Stream validChainWorkForV1 () { + return Stream.of( + Arguments.of(BigInteger.ZERO), // no work + Arguments.of(BigInteger.ONE), // small work + Arguments.of(BigInteger.valueOf(Long.MAX_VALUE)), // a larg-ish work + Arguments.of(MAX_WORK_V1) + ); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV2") + void put_postRskip_whenChainWorkAnySizeUnder32Bytes_shouldStoreBlock(BigInteger chainWork) { + int expectedCompactSerializedSize = COMPACT_SERIALIZED_SIZE_V2; + + arrange(lovellActivations); + StoredBlock storedBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + repositoryBtcBlockStoreWithCache.put(storedBlock); + + // assert + Sha256Hash expectedHash = storedBlock.getHeader().getHash(); + + ByteBuffer byteBufferForExpectedBlock = ByteBuffer.allocate(expectedCompactSerializedSize); + storedBlock.serializeCompactV2(byteBufferForExpectedBlock); + + byte[] expectedSerializedBlock = byteBufferForExpectedBlock.array(); + verify(repository, times(1)).addStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()), + expectedSerializedBlock + ); + byte[] actualSerializedBlock = repository.getStorageBytes(BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString())); + Assertions.assertNotNull(actualSerializedBlock); + Assertions.assertEquals(expectedCompactSerializedSize, actualSerializedBlock.length); + + Assertions.assertArrayEquals(expectedSerializedBlock, actualSerializedBlock); + } + + private static Stream validChainWorkForV2() { + return Stream.of( + Arguments.of(BigInteger.ZERO), // no work + Arguments.of(BigInteger.ONE), // small work + Arguments.of(BigInteger.valueOf(Long.MAX_VALUE)), // a larg-ish work + Arguments.of(MAX_WORK_V1), + Arguments.of(TOO_LARGE_WORK_V1), + Arguments.of(MAX_WORK_V2) + ); + } + + @ParameterizedTest() + @MethodSource("invalidChainWorkForV2") + void put_postRskip_whenInvalidChainWorkForV2_shouldFail(BigInteger chainWork) { + arrange(lovellActivations); + StoredBlock storedBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + IllegalArgumentException actualException = Assertions.assertThrows( + IllegalArgumentException.class, () -> { + repositoryBtcBlockStoreWithCache.put(storedBlock); + }); + + String expectedMessage = "The given number does not fit in 32"; + String actualMessage = actualException.getMessage(); + Assertions.assertEquals(expectedMessage, actualMessage); + } + + private static Stream invalidChainWorkForV2() { + return Stream.of( + Arguments.of(TOO_LARGE_WORK_V2) + ); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void get_preRskip_whenValidChainWorkForV1_shouldGetStoredBlock(BigInteger chainWork) { + arrange(arrowHeadActivations); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + Sha256Hash expectedHash = expectedStoreBlock.getHeader().getHash(); + arrangeRepositoryWithExpectedStoredBlock(expectedStoreBlock); + + // act + StoredBlock actualStoreBlock = repositoryBtcBlockStoreWithCache.get(expectedHash); + + // assert + verify(repository, times(1)).getStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()) + ); + + Assertions.assertEquals(expectedStoreBlock, actualStoreBlock); + } + + private void arrangeRepositoryWithExpectedStoredBlock(StoredBlock expectedStoreBlock) { + Sha256Hash expectedHash = expectedStoreBlock.getHeader().getHash(); + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_LEGACY); + expectedStoreBlock.serializeCompactLegacy(byteBuffer); + when(repository.getStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()) + )).thenReturn(byteBuffer.array()); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void get_postRskip_whenChainWorkForV1_shouldGetStoredBlock(BigInteger chainWork) { + arrange(lovellActivations); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + Sha256Hash expectedHash = expectedStoreBlock.getHeader().getHash(); + arrangeRepositoryWithExpectedStoredBlockV2(expectedStoreBlock); + + // act + StoredBlock actualStoreBlock = repositoryBtcBlockStoreWithCache.get(expectedHash); + + // assert + verify(repository, times(1)).getStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()) + ); + + Assertions.assertEquals(expectedStoreBlock, actualStoreBlock); + } + + private void arrangeRepositoryWithExpectedStoredBlockV2(StoredBlock expectedStoreBlock) { + Sha256Hash expectedHash = expectedStoreBlock.getHeader().getHash(); + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_V2); + expectedStoreBlock.serializeCompactV2(byteBuffer); + when(repository.getStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()) + )).thenReturn(byteBuffer.array()); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV2") + void get_postRskip_whenStoredBLochHasChainWorkOver12Bytes_shouldGetStoredBlock(BigInteger chainWork) { + arrange(lovellActivations); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + Sha256Hash expectedHash = expectedStoreBlock.getHeader().getHash(); + arrangeRepositoryWithExpectedStoredBlockV2(expectedStoreBlock); + + // act + StoredBlock actualStoreBlock = repositoryBtcBlockStoreWithCache.get(expectedHash); + + // assert + verify(repository, times(1)).getStorageBytes( + BRIDGE_ADDR, + DataWord.valueFromHex(expectedHash.toString()) + ); + + Assertions.assertEquals(expectedStoreBlock, actualStoreBlock); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void getChainHead_preRskip_whenValidChainWorkForV1_shouldGetChainHead(BigInteger chainWork) { + arrange(arrowHeadActivations); + reset(repository); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + arrangeRepositoryWithExpectedChainHead(expectedStoreBlock); + + // act + StoredBlock actualStoreBlock = repositoryBtcBlockStoreWithCache.getChainHead(); + + // assert + verify(repository, times(1)).getStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY) + ); + + Assertions.assertEquals(expectedStoreBlock, actualStoreBlock); + } + + private void arrangeRepositoryWithExpectedChainHead(StoredBlock expectedStoreBlock) { + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_LEGACY); + expectedStoreBlock.serializeCompactLegacy(byteBuffer); + when(repository.getStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY)) + ).thenReturn(byteBuffer.array()); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void getChainHead_postRskip_whenChainWorkForV1_shouldGetChainHead(BigInteger chainWork) { + arrange(lovellActivations); + reset(repository); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + arrangeRepositoryWithChainHeadV2(expectedStoreBlock); + + // act + StoredBlock actualStoreBlock = repositoryBtcBlockStoreWithCache.getChainHead(); + + // assert + verify(repository, times(1)).getStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY) + ); + + Assertions.assertEquals(expectedStoreBlock, actualStoreBlock); + } + + private void arrangeRepositoryWithChainHeadV2(StoredBlock expectedStoreBlock) { + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_V2); + expectedStoreBlock.serializeCompactV2(byteBuffer); + when(repository.getStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY)) + ).thenReturn(byteBuffer.array()); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV2") + void getChainHead_postRskip_whenStoredBLochHasChainWorkOver12Bytes_shouldGetChainHead(BigInteger chainWork) { + arrange(lovellActivations); + reset(repository); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + arrangeRepositoryWithChainHeadV2(expectedStoreBlock); + + // act + StoredBlock actualStoreBlock = repositoryBtcBlockStoreWithCache.getChainHead(); + + // assert + verify(repository, times(1)).getStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY) + ); + + Assertions.assertEquals(expectedStoreBlock, actualStoreBlock); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void setChainHead_preRskip_whenValidChainWorkForV1_shouldStoreChainHead(BigInteger chainWork) { + arrange(arrowHeadActivations); + reset(repository); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + repositoryBtcBlockStoreWithCache.setChainHead(expectedStoreBlock); + + // assert + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_LEGACY); + expectedStoreBlock.serializeCompactLegacy(byteBuffer); + + verify(repository, times(1)).addStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY), + byteBuffer.array() + ); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV1") + void setChainHead_postRskip_whenChainWorkForV1_shouldStoreChainHead(BigInteger chainWork) { + arrange(lovellActivations); + reset(repository); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + repositoryBtcBlockStoreWithCache.setChainHead(expectedStoreBlock); + + // assert + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_V2); + expectedStoreBlock.serializeCompactV2(byteBuffer); + + verify(repository, times(1)).addStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY), + byteBuffer.array() + ); + } + + @ParameterizedTest() + @MethodSource("validChainWorkForV2") + void setChainHead_postRskip_whenStoredBLochHasChainWorkOver12Bytes1_shouldStoreChainHead(BigInteger chainWork) { + arrange(lovellActivations); + reset(repository); + StoredBlock expectedStoreBlock = new StoredBlock(BLOCK, chainWork, blockHeight); + + // act + repositoryBtcBlockStoreWithCache.setChainHead(expectedStoreBlock); + + // assert + ByteBuffer byteBuffer = ByteBuffer.allocate(COMPACT_SERIALIZED_SIZE_V2); + expectedStoreBlock.serializeCompactV2(byteBuffer); + + verify(repository, times(1)).addStorageBytes( + BRIDGE_ADDR, + DataWord.fromString(BLOCK_STORE_CHAIN_HEAD_KEY), + byteBuffer.array() + ); + } +} \ No newline at end of file