From b5dbef4f0f58d73e3f6f782411fa141d31c6017d Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Wed, 27 Nov 2024 22:25:13 -0400 Subject: [PATCH 1/3] Add support to 32-byte chainwork to ThinConverter class --- build.gradle | 4 +- .../rsk/federate/adapter/ThinConverter.java | 13 +- .../federate/adapter/ThinConverterTest.java | 121 ++++++++++++++---- 3 files changed, 104 insertions(+), 34 deletions(-) diff --git a/build.gradle b/build.gradle index f1e96b880..4ededde02 100644 --- a/build.gradle +++ b/build.gradle @@ -61,8 +61,8 @@ tasks.withType(AbstractArchiveTask) { } ext { - bitcoinjcoreVersion = '0.15.6-rsk-3' - bitcoinjVersion = '0.14.4-rsk-16' + bitcoinjcoreVersion = '0.15.6-rsk-4-SNAPSHOT' + bitcoinjVersion = '0.14.4-rsk-16-SNAPSHOT' commonsLang3Version = '3.12.0' commonsIoVersion = '2.11.0' slf4jVersion = '1.7.36' diff --git a/src/main/java/co/rsk/federate/adapter/ThinConverter.java b/src/main/java/co/rsk/federate/adapter/ThinConverter.java index 793fbd311..504c5526e 100644 --- a/src/main/java/co/rsk/federate/adapter/ThinConverter.java +++ b/src/main/java/co/rsk/federate/adapter/ThinConverter.java @@ -3,6 +3,7 @@ import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.Context; import co.rsk.bitcoinj.core.NetworkParameters; +import co.rsk.bitcoinj.core.StoredBlock; import co.rsk.peg.constants.BridgeConstants; import java.nio.ByteBuffer; @@ -20,20 +21,20 @@ public static org.bitcoinj.core.StoredBlock toOriginalInstance(co.rsk.bitcoinj.c if (storedBlock == null) { return null; } - ByteBuffer buffer = ByteBuffer.allocate(96); - storedBlock.serializeCompact(buffer); + ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE_V2); + storedBlock.serializeCompactV2(buffer); buffer.flip(); - return org.bitcoinj.core.StoredBlock.deserializeCompact(org.bitcoinj.core.NetworkParameters.fromID(bridgeConstants.getBtcParamsString()), buffer); + return org.bitcoinj.core.StoredBlock.deserializeCompactV2(org.bitcoinj.core.NetworkParameters.fromID(bridgeConstants.getBtcParamsString()), buffer); } public static co.rsk.bitcoinj.core.StoredBlock toThinInstance(org.bitcoinj.core.StoredBlock storedBlock, BridgeConstants bridgeConstants) { if (storedBlock == null) { return null; } - ByteBuffer buffer = ByteBuffer.allocate(96); - storedBlock.serializeCompact(buffer); + ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE_V2); + storedBlock.serializeCompactV2(buffer); buffer.flip(); - return co.rsk.bitcoinj.core.StoredBlock.deserializeCompact(bridgeConstants.getBtcParams(), buffer); + return co.rsk.bitcoinj.core.StoredBlock.deserializeCompactV2(bridgeConstants.getBtcParams(), buffer); } diff --git a/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java b/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java index efd7076ad..1427e9bbf 100644 --- a/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java +++ b/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java @@ -4,15 +4,58 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import co.rsk.bitcoinj.core.NetworkParameters; +import co.rsk.peg.constants.BridgeConstants; +import co.rsk.peg.constants.BridgeMainNetConstants; import co.rsk.peg.constants.BridgeRegTestConstants; import java.math.BigInteger; import java.util.ArrayList; +import java.util.stream.Stream; +import org.bitcoinj.core.ECKey; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class ThinConverterTest { + private static final BigInteger NEGATIVE_CHAIN_WORK = BigInteger.valueOf(-1); + private static final BigInteger EIGHT_BYTES_WORK_V1 = new BigInteger("ffffffffffffffff", 16); // 8 bytes + private static final BigInteger MAX_WORK_V1 = new BigInteger(/* 12 bytes */ "ffffffffffffffffffffffff", 16); + private static final BigInteger MAX_WORK_V2 = new BigInteger(/* 32 bytes */ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); + private static final BigInteger TOO_LARGE_WORK_V1 = new BigInteger(/* 13 bytes */ "ffffffffffffffffffffffffff", 16); + private static final BigInteger TOO_LARGE_WORK_V2 = new BigInteger(/* 33 bytes */ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); + + private static final int height = 200; private static int nhash = 0; + private static final BridgeConstants bridgeConstants = BridgeMainNetConstants.getInstance(); + org.bitcoinj.core.NetworkParameters bitcoinCoreParams = org.bitcoinj.params.MainNetParams.get(); + co.rsk.bitcoinj.core.NetworkParameters bitcoinjThinParams = co.rsk.bitcoinj.params.MainNetParams.get(); + + private static final ECKey userKey = ECKey.fromPrivate(BigInteger.valueOf(100)); + + public static Stream validChainWorkArgsProvider() { + return Stream.of( + Arguments.of(BigInteger.ZERO), + Arguments.of(BigInteger.ONE), + Arguments.of(EIGHT_BYTES_WORK_V1), + Arguments.of(MAX_WORK_V1), + Arguments.of(TOO_LARGE_WORK_V1), + Arguments.of(MAX_WORK_V2) + ); + } + + public static Stream invalidChainWorkArgsProvider() { + return Stream.of( + Arguments.of(NEGATIVE_CHAIN_WORK), + Arguments.of(TOO_LARGE_WORK_V2) + ); + } + private org.bitcoinj.core.Sha256Hash createOriginalHash() { byte[] bytes = new byte[32]; bytes[0] = (byte) nhash++; @@ -25,61 +68,87 @@ private co.rsk.bitcoinj.core.Sha256Hash createThinHash() { return co.rsk.bitcoinj.core.Sha256Hash.wrap(bytes); } - @Test - void toThinInstanceStoredBlock() { - org.bitcoinj.core.NetworkParameters params = org.bitcoinj.params.RegTestParams.get(); - BigInteger chainWork = BigInteger.TEN; - int height = 200; - org.bitcoinj.core.Block originalBlock = new org.bitcoinj.core.Block(params, 1, createOriginalHash(), createOriginalHash(), 1000, 2000, 3000, new ArrayList<>()); + @ParameterizedTest + @MethodSource("validChainWorkArgsProvider") + void toThinInstanceStoredBlock(BigInteger chainWork) { + // arrange + org.bitcoinj.core.Block originalBlock = new org.bitcoinj.core.Block(bitcoinCoreParams, 1, createOriginalHash(), createOriginalHash(), 1000, 2000, 3000, new ArrayList<>()); org.bitcoinj.core.StoredBlock originalStoredBlock = new org.bitcoinj.core.StoredBlock(originalBlock, chainWork, height); - co.rsk.bitcoinj.core.StoredBlock thinStoredBlock = ThinConverter.toThinInstance(originalStoredBlock, new BridgeRegTestConstants()); + + // act + co.rsk.bitcoinj.core.StoredBlock thinStoredBlock = ThinConverter.toThinInstance(originalStoredBlock, bridgeConstants); + + // assert assertEquals(chainWork, thinStoredBlock.getChainWork()); assertEquals(height, thinStoredBlock.getHeight()); assertArrayEquals(originalBlock.bitcoinSerialize(), thinStoredBlock.getHeader().bitcoinSerialize()); + assertNull(ThinConverter.toThinInstance(null, bridgeConstants)); + } + + @ParameterizedTest + @MethodSource("invalidChainWorkArgsProvider") + void toThinInstanceStored_whenInvalidChainWork_shouldFail(BigInteger chainWork) { + // arrange + org.bitcoinj.core.Block originalBlock = new org.bitcoinj.core.Block(bitcoinCoreParams, 1, createOriginalHash(), createOriginalHash(), 1000, 2000, 3000, new ArrayList<>()); + org.bitcoinj.core.StoredBlock originalStoredBlock = new org.bitcoinj.core.StoredBlock(originalBlock, chainWork, height); - assertNull(ThinConverter.toThinInstance(null, new BridgeRegTestConstants())); + // act and assert + Assertions.assertThrows(IllegalArgumentException.class, () -> ThinConverter.toThinInstance(originalStoredBlock, bridgeConstants)); } - @Test - void toOriginalInstanceStoredBlock() { - co.rsk.bitcoinj.core.NetworkParameters params = co.rsk.bitcoinj.params.RegTestParams.get(); - BigInteger chainWork = BigInteger.TEN; - int height = 200; - co.rsk.bitcoinj.core.BtcBlock thinBlock = new co.rsk.bitcoinj.core.BtcBlock(params, 1, createThinHash(), createThinHash(), 1000, 2000, 3000, new ArrayList<>()); + @ParameterizedTest + @MethodSource("validChainWorkArgsProvider") + void toOriginalInstanceStoredBlock(BigInteger chainWork) { + // arrange + co.rsk.bitcoinj.core.BtcBlock thinBlock = new co.rsk.bitcoinj.core.BtcBlock(bitcoinjThinParams, 1, createThinHash(), createThinHash(), 1000, 2000, 3000, new ArrayList<>()); co.rsk.bitcoinj.core.StoredBlock thinStoredBlock = new co.rsk.bitcoinj.core.StoredBlock(thinBlock, chainWork, height); - org.bitcoinj.core.StoredBlock originalStoredBlock = ThinConverter.toOriginalInstance(thinStoredBlock, new BridgeRegTestConstants()); + + // act + org.bitcoinj.core.StoredBlock originalStoredBlock = ThinConverter.toOriginalInstance(thinStoredBlock, bridgeConstants); + + // assert assertEquals(chainWork, originalStoredBlock.getChainWork()); assertEquals(height, originalStoredBlock.getHeight()); assertArrayEquals(thinBlock.bitcoinSerialize(), originalStoredBlock.getHeader().bitcoinSerialize()); + assertNull(ThinConverter.toOriginalInstance(null, bridgeConstants)); + } + + @ParameterizedTest + @MethodSource("invalidChainWorkArgsProvider") + void toOriginalInstanceStoredBlock_whenInvalidChainWork_shouldFail(BigInteger chainWork) { + // arrange + co.rsk.bitcoinj.core.BtcBlock thinBlock = new co.rsk.bitcoinj.core.BtcBlock(bitcoinjThinParams, 1, createThinHash(), createThinHash(), 1000, 2000, 3000, new ArrayList<>()); + co.rsk.bitcoinj.core.StoredBlock thinStoredBlock = new co.rsk.bitcoinj.core.StoredBlock(thinBlock, chainWork, height); - assertNull(ThinConverter.toOriginalInstance(null, new BridgeRegTestConstants())); + // act and assert + Assertions.assertThrows(IllegalArgumentException.class, () -> ThinConverter.toOriginalInstance(thinStoredBlock, bridgeConstants)); } @Test void toOriginalInstanceNetworkParameters() { - org.bitcoinj.core.NetworkParameters originalParams = ThinConverter.toOriginalInstance(co.rsk.bitcoinj.core.NetworkParameters.ID_REGTEST); - assertEquals(co.rsk.bitcoinj.core.NetworkParameters.ID_REGTEST, originalParams.getId()); + org.bitcoinj.core.NetworkParameters originalParams = ThinConverter.toOriginalInstance( + NetworkParameters.ID_MAINNET); + assertEquals(co.rsk.bitcoinj.core.NetworkParameters.ID_MAINNET, originalParams.getId()); } @Test void toOriginalInstanceTransaction() { - co.rsk.bitcoinj.core.NetworkParameters params = co.rsk.bitcoinj.params.RegTestParams.get(); - co.rsk.bitcoinj.core.BtcTransaction thinTx = new co.rsk.bitcoinj.core.BtcTransaction(params); + co.rsk.bitcoinj.core.BtcTransaction thinTx = new co.rsk.bitcoinj.core.BtcTransaction(bitcoinjThinParams); co.rsk.bitcoinj.script.Script script = new co.rsk.bitcoinj.script.Script(new byte[]{0}); thinTx.addInput(createThinHash(), 1, script); - thinTx.addOutput(co.rsk.bitcoinj.core.Coin.CENT, co.rsk.bitcoinj.core.Address.fromBase58(params, "mhxk5q8QdGFoaP4SJ3DPtXjrbxAgxjNm3C")); - org.bitcoinj.core.Transaction originalTx = ThinConverter.toOriginalInstance(params.getId(), thinTx); + thinTx.addOutput(co.rsk.bitcoinj.core.Coin.CENT, co.rsk.bitcoinj.core.Address.fromP2SHHash(bitcoinjThinParams, userKey.getPubKeyHash())); + org.bitcoinj.core.Transaction originalTx = ThinConverter.toOriginalInstance(bitcoinjThinParams.getId(), thinTx); assertEquals(thinTx.getHash().toString(), originalTx.getTxId().toString()); - co.rsk.bitcoinj.core.BtcTransaction thinnTx2 = ThinConverter.toThinInstance(params, originalTx); + co.rsk.bitcoinj.core.BtcTransaction thinnTx2 = ThinConverter.toThinInstance(bitcoinjThinParams, originalTx); assertEquals(thinTx.getHash(), thinnTx2.getHash()); } @Test void toOriginalInstanceAddress() { - co.rsk.bitcoinj.core.NetworkParameters params = co.rsk.bitcoinj.params.RegTestParams.get(); - co.rsk.bitcoinj.core.Address thinAddress = co.rsk.bitcoinj.core.Address.fromBase58(params, "mhxk5q8QdGFoaP4SJ3DPtXjrbxAgxjNm3C"); - org.bitcoinj.core.Address originalAddress = ThinConverter.toOriginalInstance(ThinConverter.toOriginalInstance(co.rsk.bitcoinj.core.NetworkParameters.ID_REGTEST), thinAddress); + co.rsk.bitcoinj.core.Address thinAddress = co.rsk.bitcoinj.core.Address.fromP2SHHash(bitcoinjThinParams, userKey.getPubKeyHash()); + org.bitcoinj.core.Address originalAddress = ThinConverter.toOriginalInstance(ThinConverter.toOriginalInstance( + bitcoinjThinParams.getId()), thinAddress); assertEquals(thinAddress.toString(), originalAddress.toString()); } } From 522777775194e914c94452e2fa0b31494434d87d Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Thu, 28 Nov 2024 21:30:53 -0400 Subject: [PATCH 2/3] - Moved null assertion to its own test. - Renamed EIGHT_BYTES_WORK_V1 constant to BELOW_MAX_WORK_V1 --- .../rsk/federate/adapter/ThinConverterTest.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java b/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java index 1427e9bbf..b245deca6 100644 --- a/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java +++ b/src/test/java/co/rsk/federate/adapter/ThinConverterTest.java @@ -7,7 +7,6 @@ import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.peg.constants.BridgeConstants; import co.rsk.peg.constants.BridgeMainNetConstants; -import co.rsk.peg.constants.BridgeRegTestConstants; import java.math.BigInteger; import java.util.ArrayList; import java.util.stream.Stream; @@ -21,7 +20,7 @@ class ThinConverterTest { private static final BigInteger NEGATIVE_CHAIN_WORK = BigInteger.valueOf(-1); - private static final BigInteger EIGHT_BYTES_WORK_V1 = new BigInteger("ffffffffffffffff", 16); // 8 bytes + private static final BigInteger BELOW_MAX_WORK_V1 = new BigInteger("ffffffffffffffff", 16); // 8 bytes private static final BigInteger MAX_WORK_V1 = new BigInteger(/* 12 bytes */ "ffffffffffffffffffffffff", 16); private static final BigInteger MAX_WORK_V2 = new BigInteger(/* 32 bytes */ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); @@ -42,7 +41,7 @@ public static Stream validChainWorkArgsProvider() { return Stream.of( Arguments.of(BigInteger.ZERO), Arguments.of(BigInteger.ONE), - Arguments.of(EIGHT_BYTES_WORK_V1), + Arguments.of(BELOW_MAX_WORK_V1), Arguments.of(MAX_WORK_V1), Arguments.of(TOO_LARGE_WORK_V1), Arguments.of(MAX_WORK_V2) @@ -82,7 +81,6 @@ void toThinInstanceStoredBlock(BigInteger chainWork) { assertEquals(chainWork, thinStoredBlock.getChainWork()); assertEquals(height, thinStoredBlock.getHeight()); assertArrayEquals(originalBlock.bitcoinSerialize(), thinStoredBlock.getHeader().bitcoinSerialize()); - assertNull(ThinConverter.toThinInstance(null, bridgeConstants)); } @ParameterizedTest @@ -96,6 +94,11 @@ void toThinInstanceStored_whenInvalidChainWork_shouldFail(BigInteger chainWork) Assertions.assertThrows(IllegalArgumentException.class, () -> ThinConverter.toThinInstance(originalStoredBlock, bridgeConstants)); } + @Test + void toThinInstanceStoredBlock_whenPassingNull_shouldReturnNull() { + assertNull(ThinConverter.toThinInstance(null, bridgeConstants)); + } + @ParameterizedTest @MethodSource("validChainWorkArgsProvider") void toOriginalInstanceStoredBlock(BigInteger chainWork) { @@ -110,7 +113,6 @@ void toOriginalInstanceStoredBlock(BigInteger chainWork) { assertEquals(chainWork, originalStoredBlock.getChainWork()); assertEquals(height, originalStoredBlock.getHeight()); assertArrayEquals(thinBlock.bitcoinSerialize(), originalStoredBlock.getHeader().bitcoinSerialize()); - assertNull(ThinConverter.toOriginalInstance(null, bridgeConstants)); } @ParameterizedTest @@ -124,6 +126,11 @@ void toOriginalInstanceStoredBlock_whenInvalidChainWork_shouldFail(BigInteger ch Assertions.assertThrows(IllegalArgumentException.class, () -> ThinConverter.toOriginalInstance(thinStoredBlock, bridgeConstants)); } + @Test + void toOriginalInstance_whenPassingNull_shouldReturnNull() { + assertNull(ThinConverter.toOriginalInstance(null, bridgeConstants)); + } + @Test void toOriginalInstanceNetworkParameters() { org.bitcoinj.core.NetworkParameters originalParams = ThinConverter.toOriginalInstance( From 877b3ce324a694b7b0f75b21b1032d0d111336bc Mon Sep 17 00:00:00 2001 From: nathanieliov Date: Wed, 4 Dec 2024 09:58:52 -0400 Subject: [PATCH 3/3] Bump version to 0.14.4-rsk-17-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4ededde02..1212c8dcf 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,7 @@ tasks.withType(AbstractArchiveTask) { ext { bitcoinjcoreVersion = '0.15.6-rsk-4-SNAPSHOT' - bitcoinjVersion = '0.14.4-rsk-16-SNAPSHOT' + bitcoinjVersion = '0.14.4-rsk-17-SNAPSHOT' commonsLang3Version = '3.12.0' commonsIoVersion = '2.11.0' slf4jVersion = '1.7.36'