Skip to content

Commit

Permalink
Merge pull request #359 from rsksmart/add-32-byte-chainwork-support
Browse files Browse the repository at this point in the history
Add support to 32-byte chainwork to ThinConverter class
  • Loading branch information
marcos-iov authored Dec 5, 2024
2 parents 6a99ec5 + 877b3ce commit 1d082b2
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 34 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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-17-SNAPSHOT'
commonsLang3Version = '3.12.0'
commonsIoVersion = '2.11.0'
slf4jVersion = '1.7.36'
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/co/rsk/federate/adapter/ThinConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}


Expand Down
128 changes: 102 additions & 26 deletions src/test/java/co/rsk/federate/adapter/ThinConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,57 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import co.rsk.peg.constants.BridgeRegTestConstants;
import co.rsk.bitcoinj.core.NetworkParameters;
import co.rsk.peg.constants.BridgeConstants;
import co.rsk.peg.constants.BridgeMainNetConstants;
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 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);
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<Arguments> validChainWorkArgsProvider() {
return Stream.of(
Arguments.of(BigInteger.ZERO),
Arguments.of(BigInteger.ONE),
Arguments.of(BELOW_MAX_WORK_V1),
Arguments.of(MAX_WORK_V1),
Arguments.of(TOO_LARGE_WORK_V1),
Arguments.of(MAX_WORK_V2)
);
}

public static Stream<Arguments> 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++;
Expand All @@ -25,61 +67,95 @@ 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, new BridgeRegTestConstants()));
@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);

// 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<>());
void toThinInstanceStoredBlock_whenPassingNull_shouldReturnNull() {
assertNull(ThinConverter.toThinInstance(null, bridgeConstants));
}

@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, new BridgeRegTestConstants()));
@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);

// act and assert
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(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());
}
}

0 comments on commit 1d082b2

Please sign in to comment.