From bbd0b17d551c9044554a15ba873a8b8d27c3c9d9 Mon Sep 17 00:00:00 2001 From: matthewjames Date: Sat, 5 Oct 2024 17:52:03 -0400 Subject: [PATCH] Add @Disabled tests to double check solana4j implementations are "correct" --- build.gradle | 8 ++- src/jmh/README.md | 10 ---- .../lmax/solana4j/Sha256HashingBenchmark.java | 43 ------------- .../com/lmax/solana4j/util/Base58Test.java | 60 ++++++++++++++++--- .../com/lmax/solana4j/util/Ed25519Test.java | 44 ++++++++++++++ 5 files changed, 101 insertions(+), 64 deletions(-) delete mode 100644 src/jmh/java/com/lmax/solana4j/Sha256HashingBenchmark.java diff --git a/build.gradle b/build.gradle index 77986d3..c64dadc 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,7 @@ dependencies { // test support dependencies testSupportImplementation 'org.testcontainers:testcontainers:1.19.8' testSupportImplementation 'com.lmax:simple-dsl:3.0.0' - testSupportImplementation 'org.assertj:assertj-core:3.25.3' + testSupportImplementation 'org.assertj:assertj-core:3.26.3' testSupportImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testSupportImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2' testSupportImplementation 'com.fasterxml.jackson.core:jackson-core:2.17.2' @@ -83,17 +83,19 @@ dependencies { testSupportImplementation 'org.bouncycastle:bcprov-jdk18on:1.78.1' // test dependencies - testImplementation 'org.assertj:assertj-core:3.25.3' + testImplementation 'org.assertj:assertj-core:3.26.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2' testImplementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.23.1' testImplementation 'org.slf4j:slf4j-api:2.0.13' testImplementation 'org.bouncycastle:bcprov-jdk18on:1.78.1' + testImplementation 'net.i2p.crypto:eddsa:0.3.0' + testImplementation 'org.bitcoinj:bitcoinj-core:0.16.3' // integration test dependencies integrationTestImplementation 'org.testcontainers:testcontainers:1.19.8' integrationTestImplementation 'com.lmax:simple-dsl:3.0.0' - integrationTestImplementation 'org.assertj:assertj-core:3.25.3' + integrationTestImplementation 'org.assertj:assertj-core:3.26.3' integrationTestImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' integrationTestImplementation 'com.fasterxml.jackson.core:jackson-core:2.17.2' integrationTestImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2' diff --git a/src/jmh/README.md b/src/jmh/README.md index 7290a30..1945e66 100644 --- a/src/jmh/README.md +++ b/src/jmh/README.md @@ -32,15 +32,5 @@ EddsaPointOnCurveBenchmark.pointOnCurveEddsaImplementation thrpt 10 8 EddsaPointOnCurveBenchmark.pointOnCurveSolana4jImplementation thrpt 10 84799.239 ± 5135.192 ops/s ``` -#### Sha256 Hashing - -Dependency Replaced: `org.bitcoinj:bitcoinj-core:0.16.3` -Benchmark Written: `Sha256HashingBenchmark` - -```text -Benchmark Mode Cnt Score Error Units -Sha256HashingBenchmark.sha256HashBitcoinjImplementation thrpt 10 9082306.173 ± 56286.482 ops/s -Sha256HashingBenchmark.sha256HashSolana4jImplementation thrpt 10 9184423.015 ± 18583.452 ops/s -``` diff --git a/src/jmh/java/com/lmax/solana4j/Sha256HashingBenchmark.java b/src/jmh/java/com/lmax/solana4j/Sha256HashingBenchmark.java deleted file mode 100644 index 4848c22..0000000 --- a/src/jmh/java/com/lmax/solana4j/Sha256HashingBenchmark.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.lmax.solana4j; - -import com.lmax.solana4j.util.Sha256; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.SECONDS) -@Warmup(iterations = 5, time = 1) -@Measurement(iterations = 5, time = 1) -@Fork(2) -@Threads(1) -@State(Scope.Thread) -public class Sha256HashingBenchmark -{ - private static final String RANDOM_STRING = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - private static final byte[] BYTES = RANDOM_STRING.getBytes(StandardCharsets.UTF_8); - - @Benchmark - public void sha256HashSolana4jImplementation(final Blackhole bh) - { - bh.consume(Sha256.hash(BYTES)); - } - - @Benchmark - public void sha256HashBitcoinjImplementation(final Blackhole bh) - { - bh.consume(org.bitcoinj.core.Sha256Hash.hash(BYTES)); - } - -} \ No newline at end of file diff --git a/src/test/java/com/lmax/solana4j/util/Base58Test.java b/src/test/java/com/lmax/solana4j/util/Base58Test.java index a311b10..0025d9b 100644 --- a/src/test/java/com/lmax/solana4j/util/Base58Test.java +++ b/src/test/java/com/lmax/solana4j/util/Base58Test.java @@ -1,11 +1,13 @@ package com.lmax.solana4j.util; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.util.Random; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class Base58Test @@ -38,7 +40,7 @@ void validBase58StringShouldDecode() final var decode = Base58.decode(validBase58String); - assertThat(decode).isEqualTo(new byte[] { 100, 6, 2 }); + assertThat(decode).isEqualTo(new byte[]{100, 6, 2}); } @Test @@ -61,7 +63,7 @@ void shouldDecodeAndEncodeBackToOriginal() @Test void shouldEncodeAndDecodeBackToOriginal() { - final var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + final var bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; final var encode = Base58.encode(bytes); final var decode = Base58.decode(encode); @@ -72,7 +74,7 @@ void shouldEncodeAndDecodeBackToOriginal() @Test void leadingZerosShouldBeEncodedAsCharacter1() { - final var leadingZeros = new byte[] { 0, 0, 1 }; + final var leadingZeros = new byte[]{0, 0, 1}; final var encode = Base58.encode(leadingZeros); @@ -82,7 +84,7 @@ void leadingZerosShouldBeEncodedAsCharacter1() @Test void singleZeroByteEncodedAsCharacter1() { - final var singleZero = new byte[] { 0 }; + final var singleZero = new byte[]{0}; final var encode = Base58.encode(singleZero); @@ -92,7 +94,7 @@ void singleZeroByteEncodedAsCharacter1() @Test void handlesLargestSingleByteValue() { - final var largestByteValue = new byte[] { (byte) 0xff }; + final var largestByteValue = new byte[]{(byte) 0xff}; final var encode = Base58.encode(largestByteValue); @@ -102,7 +104,7 @@ void handlesLargestSingleByteValue() @Test void handlesManyLargestByteValues() { - final var largestByteValue = new byte[] { + final var largestByteValue = new byte[]{ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; @@ -145,16 +147,58 @@ void handlesArbitraryNonAsciiCharacters() assertThat(new String(decode, StandardCharsets.UTF_8)).isEqualTo("测试"); } - private byte[] createRandomByteArray(final int length) + @Test + @Disabled("Just to check the implementations do provide the same results.") + void solana4jImplementationBase58EncodeShouldMatchBitcoinjLibrary() + { + for (int i = 0; i < 100000; i++) + { + final var randomByteArray = createRandomByteArray(32); + + final String base58EncodeSolana4jImplementation = Base58.encode(randomByteArray); + final String base58EncodeBitcoinjImplementation = org.bitcoinj.core.Base58.encode(randomByteArray); + + assertEquals(base58EncodeSolana4jImplementation, base58EncodeBitcoinjImplementation); + } + } + + @Test + @Disabled("Just to check the implementations do provide the same results.") + void solana4jImplementationBase58DecodeShouldMatchBitcoinjLibrary() + { + for (int i = 0; i < 100000; i++) + { + final var randomString = createRandomBase58String(); + + final byte[] base58DecodeSolana4jImplementation = Base58.decode(randomString); + final byte[] base58DecodeBitcoinjImplementation = org.bitcoinj.core.Base58.decode(randomString); + + assertThat(base58DecodeSolana4jImplementation) + .usingDefaultElementComparator() + .isEqualTo(base58DecodeBitcoinjImplementation); + } + } + + private String createRandomBase58String() { + final char[] characters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); final Random random = new Random(); + final char[] array = new char[10]; + for (int i = 0; i < 10; i++) + { + array[i] = characters[random.nextInt(characters.length)]; + } + return new String(array); + } + private byte[] createRandomByteArray(final int length) + { + final Random random = new Random(); final byte[] array = new byte[length]; for (int i = 0; i < length; i++) { array[i] = (byte) random.nextInt(); } - return array; } } \ No newline at end of file diff --git a/src/test/java/com/lmax/solana4j/util/Ed25519Test.java b/src/test/java/com/lmax/solana4j/util/Ed25519Test.java index eb8f3e1..8d6d7ca 100644 --- a/src/test/java/com/lmax/solana4j/util/Ed25519Test.java +++ b/src/test/java/com/lmax/solana4j/util/Ed25519Test.java @@ -1,9 +1,14 @@ package com.lmax.solana4j.util; +import net.i2p.crypto.eddsa.math.GroupElement; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.math.BigInteger; +import java.util.Random; +import static net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable.ED_25519_CURVE_SPEC; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -76,4 +81,43 @@ void shouldThrowExceptionForInvalidPublicKeyLength() assertThrows(RuntimeException.class, () -> Ed25519.isOnCurve(invalidPublicKey)); } + + @Test + @Disabled("Just to check the implementations do provide the same results.") + void solana4jImplementationIsPointOnCurveShouldMatchEddsaLibrary() + { + for (int i = 0; i < 100000; i++) + { + final var randomByteArray = createRandomByteArray(32); + + final boolean onCurveEddsaLibrary = isOnCurveEddsaLibrary(randomByteArray); + final boolean onCurveSolana4jImplementation = Ed25519.isOnCurve(randomByteArray); + + assertEquals(onCurveEddsaLibrary, onCurveSolana4jImplementation); + } + } + + private byte[] createRandomByteArray(final int length) + { + final Random random = new Random(); + final byte[] array = new byte[length]; + for (int i = 0; i < length; i++) + { + array[i] = (byte) random.nextInt(); + } + return array; + } + + private boolean isOnCurveEddsaLibrary(final byte[] publickKey) + { + try + { + final GroupElement point = ED_25519_CURVE_SPEC.getCurve().createPoint(publickKey, false); + return point.isOnCurve(); + } + catch (final IllegalArgumentException e) + { + return false; + } + } } \ No newline at end of file