Skip to content

Commit

Permalink
Remove dependency on JDK 17 HexFormat
Browse files Browse the repository at this point in the history
Add ByteArray, ByteUtils, and HexFormat classes
to do it.
  • Loading branch information
msgilligan committed Sep 3, 2024
1 parent 28112bf commit 4ff1e42
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 9 deletions.
1 change: 1 addition & 0 deletions secp-api/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
requires org.jspecify;

exports org.bitcoinj.secp.api;
exports org.bitcoinj.secp.api.internal; /* TEMPORARY */

uses org.bitcoinj.secp.api.Secp256k1Provider;
}
60 changes: 60 additions & 0 deletions secp-api/src/main/java/org/bitcoinj/secp/api/ByteArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2023-2024 secp256k1-jdk Developers.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.secp.api;

import org.bitcoinj.secp.api.internal.ByteUtils;
import org.bitcoinj.secp.api.internal.HexFormat;

import java.util.Arrays;

/**
* An effectively-immutable byte array.
*/
public interface ByteArray extends Comparable<ByteArray> {
HexFormat HEX_FORMAT = new HexFormat();

/**
* @return the bytes as an array
*/
byte[] bytes();

/**
* @return the bytes as a hex-formatted string
*/
default String formatHex() {
return HEX_FORMAT.formatHex(bytes());
}

/**
* {@inheritDoc}
* <p>For {@link ByteArray} this is a byte-by-byte, unsigned comparison.
* @param o {@inheritDoc}
* @return {@inheritDoc}
*/
@Override
default int compareTo(ByteArray o) {
return ByteUtils.arrayUnsignedComparator().compare(bytes(), o.bytes());
}

/**
* Utility method to format hex bytes as string
* @param bytes bytes to format
* @return hex-formatted String
*/
static String toHexString(byte[] bytes) {
return HEX_FORMAT.formatHex(bytes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.bitcoinj.secp.api;

import java.math.BigInteger;
import java.util.HexFormat;

/**
*
Expand Down Expand Up @@ -56,7 +55,6 @@ static P256K1XOnlyPubKey of(BigInteger x) {
* Default implementation. Currently used by all known implementations
*/
class P256K1XOnlyPubKeyImpl implements P256K1XOnlyPubKey {
private static final HexFormat formatter = HexFormat.of();
private final BigInteger x;

public P256K1XOnlyPubKeyImpl(P256k1PubKey pubKey) {
Expand Down Expand Up @@ -86,7 +84,7 @@ public byte[] getSerialized() {
*/
@Override
public String toString() {
return formatter.formatHex(getSerialized());
return ByteArray.toHexString(getSerialized());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.util.HexFormat;

/**
*
*/
public interface P256k1PubKey extends ECPublicKey {
HexFormat hf = HexFormat.of();

@Override
default String getAlgorithm() {
return "Secp256k1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
/**
*
*/
public interface SignatureData {
public byte[] bytes();
public interface SignatureData extends ByteArray {
byte[] bytes();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2023-2024 secp256k1-jdk Developers.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.secp.api.internal;

import java.util.Comparator;

/**
*
*/
public class ByteUtils {
/**
* Provides a byte array comparator.
* @return A comparator for byte[]
*/
public static Comparator<byte[]> arrayUnsignedComparator() {
return ARRAY_UNSIGNED_COMPARATOR;
}

// In Java 9, this can be replaced with Arrays.compareUnsigned()
private static final Comparator<byte[]> ARRAY_UNSIGNED_COMPARATOR = (a, b) -> {
int minLength = Math.min(a.length, b.length);
for (int i = 0; i < minLength; i++) {
int result = compareUnsigned(a[i], b[i]);
if (result != 0) {
return result;
}
}
return a.length - b.length;
};

private static int compareUnsigned(byte a, byte b) {
return Byte.toUnsignedInt(a) - Byte.toUnsignedInt(b);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2023-2024 secp256k1-jdk Developers.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.bitcoinj.secp.api.internal;

/**
* This class implements a subset of the functionality of the {@code HexFormat} class available in Java 17 and later.
* Its behavior is the same as an instance of Java 17 {@code HexFormat} created with {@code HexFormat.of()}.
* It is an internal class and may be removed in the future when and if we have a Java 17 baseline. This is a copy of
* HexFormat.java in `bitcoinj-base/org.bitcoinj.base.internal`.
* <p>Thanks to this <a href="https://www.baeldung.com/java-byte-arrays-hex-strings">Baeldung article</a>.
*/
public class HexFormat {
public String formatHex(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder(bytes.length * 2);
for (byte aByte : bytes) {
stringBuilder.append(byteToHex(aByte));
}
return stringBuilder.toString();
}

public byte[] parseHex(String hexString) {
if (hexString.length() % 2 == 1) {
throw new IllegalArgumentException("Invalid hexadecimal String supplied.");
}

byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
}
return bytes;
}
private String byteToHex(byte num) {
char[] hexDigits = new char[2];
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
hexDigits[1] = Character.forDigit((num & 0xF), 16);
return new String(hexDigits);
}

private byte hexToByte(String hexString) {
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
return (byte) ((firstDigit << 4) + secondDigit);
}

private int toDigit(char hexChar) {
int digit = Character.digit(hexChar, 16);
if (digit == -1) {
throw new IllegalArgumentException("Invalid Hexadecimal Character: "+ hexChar);
}
return digit;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.bitcoinj.secp.bouncy;


import org.bitcoinj.secp.api.ByteArray;
import org.bitcoinj.secp.api.P256k1PubKey;
import org.bouncycastle.math.ec.ECPoint;

Expand Down Expand Up @@ -49,7 +50,7 @@ public byte[] getEncoded() {

@Override
public String toString() {
return hf.formatHex(bytes()) ;
return ByteArray.HEX_FORMAT.formatHex(bytes());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.bitcoinj.secp.ffm;

import org.bitcoinj.secp.api.ByteArray;
import org.bitcoinj.secp.api.SignatureData;

import java.lang.foreign.MemorySegment;
Expand Down

0 comments on commit 4ff1e42

Please sign in to comment.