Skip to content
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

Add 32 byte chainwork support #368

Merged
merged 3 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
}
}
Loading