Skip to content

Commit cf8fa10

Browse files
authored
feat: add schnorr signature (#178)
1 parent 8994f04 commit cf8fa10

File tree

178 files changed

+878
-4184
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

178 files changed

+878
-4184
lines changed

.github/workflows/test.yml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ on:
1111
jobs:
1212
format:
1313
runs-on: ubuntu-latest
14-
1514
steps:
1615
- name: Checkout code
1716
uses: actions/checkout@v2
@@ -21,11 +20,11 @@ jobs:
2120
- name: Merge Conflict finder
2221
uses: olivernybroe/[email protected]
2322

24-
- name: Use Java Version ${{ matrix.java }}
23+
- name: Use Java Version 22
2524
uses: actions/setup-java@v2
2625
with:
2726
distribution: "adopt"
28-
java-version: 22
27+
java-version: "22"
2928
cache: "gradle"
3029

3130
- name: Format code
@@ -39,10 +38,6 @@ jobs:
3938

4039
unit:
4140
runs-on: ubuntu-latest
42-
strategy:
43-
matrix:
44-
java: [11, 15, 16, 17, 18, 19, 20, 21, 22]
45-
4641
steps:
4742
- name: Checkout code
4843
uses: actions/checkout@v2
@@ -52,11 +47,23 @@ jobs:
5247
- name: Merge Conflict finder
5348
uses: olivernybroe/[email protected]
5449

55-
- name: Use Java Version ${{ matrix.java }}
50+
- name: Install Nix
51+
uses: cachix/install-nix-action@v27
52+
53+
- name: Install libsecp256k1
54+
run: |
55+
nix profile install nixpkgs#secp256k1
56+
env:
57+
NIX_PATH: $HOME/.nix-profile/bin
58+
59+
- name: Set LD_LIBRARY_PATH
60+
run: echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/.nix-profile/lib" >> $GITHUB_ENV
61+
62+
- name: Use Java Version 22
5663
uses: actions/setup-java@v2
5764
with:
58-
distribution: "zulu"
59-
java-version: ${{ matrix.java }}
65+
distribution: "adopt"
66+
java-version: "22"
6067
cache: "gradle"
6168

6269
- name: Install

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ repositories {
2424
dependencies {
2525
implementation 'org.web3j:core:4.8.7'
2626
implementation 'org.bitcoinj:bitcoinj-core:0.16.3'
27+
implementation files('libs/secp256k1-api-0.0.1.jar')
28+
implementation files('libs/secp256k1-foreign-0.0.1.jar')
2729
implementation 'com.google.code.gson:gson:2.11.0'
2830
implementation 'com.google.guava:guava:30.2.0-jre'
2931

@@ -40,6 +42,7 @@ test {
4042
useJUnitPlatform()
4143
testLogging {
4244
events 'PASSED', 'FAILED', 'SKIPPED'
45+
showStandardStreams = true
4346
}
4447
}
4548

libs/secp256k1-api-0.0.1.jar

12.4 KB
Binary file not shown.

libs/secp256k1-foreign-0.0.1.jar

92.4 KB
Binary file not shown.
Lines changed: 0 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,166 +1,6 @@
11
package org.arkecosystem.crypto;
22

3-
import java.math.BigInteger;
4-
import org.bitcoinj.core.Sha256Hash;
5-
6-
/** Heavily inspired in https://github.com/miketwk/bip-schnorr-java/blob/master/Schnorr.java */
73
public class Schnorr {
8-
public static final BigInteger p =
9-
new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
10-
public static final BigInteger n =
11-
new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
12-
public static final BigInteger[] G = {
13-
new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16),
14-
new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
15-
};
16-
17-
public static final BigInteger TWO = BigInteger.valueOf(2);
18-
public static final BigInteger THREE = BigInteger.valueOf(3);
19-
20-
private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
21-
22-
public static BigInteger[] addPoint(BigInteger[] p1, BigInteger[] p2) {
23-
if (p1 == null || p1.length != 2) return p2;
24-
25-
if (p2 == null || p2.length != 2) return p1;
26-
27-
if (p1[0].compareTo(p2[0]) == 0 && p1[1].compareTo(p2[1]) != 0) return null;
28-
29-
BigInteger lam;
30-
if (p1[0].compareTo(p2[0]) == 0 && p1[1].compareTo(p2[1]) == 0)
31-
lam =
32-
(THREE.multiply(p1[0])
33-
.multiply(p1[0])
34-
.multiply(TWO.multiply(p1[1]).modPow(p.subtract(TWO), p)))
35-
.mod(p);
36-
else
37-
lam =
38-
(p2[1].subtract(p1[1])
39-
.multiply(p2[0].subtract(p1[0]).modPow(p.subtract(TWO), p)))
40-
.mod(p);
41-
42-
BigInteger x3 = (lam.multiply(lam).subtract(p1[0]).subtract(p2[0])).mod(p);
43-
44-
return new BigInteger[] {x3, lam.multiply(p1[0].subtract(x3)).subtract(p1[1]).mod(p)};
45-
}
46-
47-
public static BigInteger[] multiplyPoint(BigInteger[] P, BigInteger n) {
48-
BigInteger[] R = null;
49-
for (int i = 0; i < 256; i++) {
50-
if (BigInteger.ONE.compareTo(n.shiftRight(i).and(BigInteger.ONE)) == 0)
51-
R = addPoint(R, P);
52-
P = addPoint(P, P);
53-
}
54-
return R;
55-
}
56-
57-
public static BigInteger jacobi(BigInteger x) {
58-
return x.modPow(p.subtract(BigInteger.ONE).divide(TWO), p);
59-
}
60-
61-
public static BigInteger[] bytesToPoint(byte[] b) {
62-
if (b[0] != 2 && b[0] != 3) return null;
63-
64-
BigInteger odd = b[0] == 3 ? BigInteger.ONE : BigInteger.ZERO;
65-
BigInteger x = toBigInteger(b, 1, 32);
66-
BigInteger y_sq = x.modPow(THREE, p).add(BigInteger.valueOf(7)).mod(p);
67-
BigInteger y0 = y_sq.modPow(p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)), p);
68-
if (y_sq.compareTo(y0.modPow(TWO, p)) != 0) return null;
69-
70-
BigInteger y = y0.and(BigInteger.ONE).compareTo(odd) != 0 ? p.subtract(y0) : y0;
71-
72-
return new BigInteger[] {x, y};
73-
}
74-
75-
public static byte[] to32BytesData(BigInteger num) {
76-
String hexNum = num.toString(16);
77-
if (hexNum.length() < 64) {
78-
StringBuilder sb = new StringBuilder();
79-
for (int i = 0; i < 64 - hexNum.length(); i++) sb.append("0");
80-
81-
hexNum = sb.append(hexNum).toString();
82-
}
83-
return hexStringToByteArray(hexNum);
84-
}
85-
86-
public static BigInteger toBigInteger(byte[] data, int startPos, int len) {
87-
return new BigInteger(bytesToHex(data, startPos, len), 16);
88-
}
89-
90-
public static BigInteger toBigInteger(byte[] data) {
91-
return new BigInteger(bytesToHex(data), 16);
92-
}
93-
94-
public static byte[] pointToBytes(BigInteger[] point) {
95-
byte[] res = new byte[33];
96-
res[0] =
97-
BigInteger.ONE.compareTo(point[1].and(BigInteger.ONE)) == 0
98-
? (byte) 0x03
99-
: (byte) 0x02;
100-
System.arraycopy(to32BytesData(point[0]), 0, res, 1, 32);
101-
return res;
102-
}
103-
104-
public static byte[] schnorrSign(byte[] msg, BigInteger seckey) {
105-
if (msg.length != 32) throw new RuntimeException("The message must be a 32-byte array.");
106-
107-
if (BigInteger.ZERO.compareTo(seckey) > 0
108-
|| seckey.compareTo(n.subtract(BigInteger.ONE)) > 0)
109-
throw new RuntimeException("The secret key must be an integer in the range 1..n-1.");
110-
111-
byte[] resultData = new byte[32 + msg.length];
112-
System.arraycopy(to32BytesData(seckey), 0, resultData, 0, 32);
113-
System.arraycopy(msg, 0, resultData, 32, msg.length);
114-
115-
BigInteger k0 = toBigInteger(Sha256Hash.hash(resultData)).mod(n);
116-
if (BigInteger.ZERO.compareTo(k0) == 0)
117-
throw new RuntimeException("Failure. This happens only with negligible probability.");
118-
119-
BigInteger[] R = multiplyPoint(G, k0);
120-
121-
BigInteger k = BigInteger.ONE.compareTo(jacobi(R[1])) != 0 ? n.subtract(k0) : k0;
122-
byte[] R0Bytes = to32BytesData(R[0]);
123-
byte[] eData = new byte[32 + 33 + 32];
124-
System.arraycopy(R0Bytes, 0, eData, 0, 32);
125-
System.arraycopy(pointToBytes(multiplyPoint(G, seckey)), 0, eData, 32, 33);
126-
System.arraycopy(msg, 0, eData, 65, 32);
127-
eData = Sha256Hash.hash(eData);
128-
BigInteger e = toBigInteger(eData).mod(n);
129-
130-
byte[] finalData = new byte[64];
131-
System.arraycopy(R0Bytes, 0, finalData, 0, 32);
132-
System.arraycopy(to32BytesData(e.multiply(seckey).add(k).mod(n)), 0, finalData, 32, 32);
133-
134-
return finalData;
135-
}
136-
137-
public static boolean schnorrVerify(byte[] msg, byte[] pubkey, byte[] sig) {
138-
if (msg.length != 32) throw new RuntimeException("The message must be a 32-byte array.");
139-
140-
if (pubkey.length != 33)
141-
throw new RuntimeException("The public key must be a 33-byte array.");
142-
143-
if (sig.length != 64) throw new RuntimeException("The signature must be a 64-byte array.");
144-
145-
BigInteger[] P = bytesToPoint(pubkey);
146-
if (P == null) return false;
147-
148-
BigInteger r = toBigInteger(sig, 0, 32);
149-
BigInteger s = toBigInteger(sig, 32, 32);
150-
151-
if (r.compareTo(p) >= 0 || s.compareTo(n) >= 0) return false;
152-
153-
byte[] eData = new byte[32 + 33 + 32];
154-
System.arraycopy(sig, 0, eData, 0, 32);
155-
System.arraycopy(pointToBytes(P), 0, eData, 32, 33);
156-
System.arraycopy(msg, 0, eData, 65, 32);
157-
eData = Sha256Hash.hash(eData);
158-
BigInteger e = toBigInteger(eData).mod(n);
159-
160-
BigInteger[] R = addPoint(multiplyPoint(G, s), multiplyPoint(P, n.subtract(e)));
161-
162-
return R != null && BigInteger.ONE.compareTo(jacobi(R[1])) == 0 && r.compareTo(R[0]) == 0;
163-
}
1644

1655
public static byte[] hexStringToByteArray(String s) {
1666
int len = s.length();
@@ -173,24 +13,4 @@ public static byte[] hexStringToByteArray(String s) {
17313
}
17414
return data;
17515
}
176-
177-
public static String bytesToHex(byte[] bytes) {
178-
char[] hexChars = new char[bytes.length * 2];
179-
for (int j = 0; j < bytes.length; j++) {
180-
int v = bytes[j] & 0xFF;
181-
hexChars[j * 2] = hexArray[v >>> 4];
182-
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
183-
}
184-
return new String(hexChars);
185-
}
186-
187-
public static String bytesToHex(byte[] bytes, int startPos, int len) {
188-
char[] hexChars = new char[len * 2];
189-
for (int j = 0, i = startPos; j < len; j++, i++) {
190-
int v = bytes[i] & 0xFF;
191-
hexChars[j * 2] = hexArray[v >>> 4];
192-
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
193-
}
194-
return new String(hexChars);
195-
}
19616
}

src/main/java/org/arkecosystem/crypto/enums/CoreTransactionTypes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
public enum CoreTransactionTypes {
44
TRANSFER(0),
55
SECOND_SIGNATURE_REGISTRATION(1),
6-
DELEGATE_REGISTRATION(2),
6+
VALIDATOR_REGISTRATION(2),
77
VOTE(3),
88
MULTI_SIGNATURE_REGISTRATION(4),
99
IPFS(5),
1010
MULTI_PAYMENT(6),
11-
DELEGATE_RESIGNATION(7),
11+
VALIDATOR_RESIGNATION(7),
1212
HTLC_LOCK(8),
1313
HTLC_CLAIM(9),
1414
HTLC_REFUND(10);

src/main/java/org/arkecosystem/crypto/enums/Fees.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
public enum Fees {
44
TRANSFER(10_000_000L),
55
SECOND_SIGNATURE_REGISTRATION(500_000_000L),
6-
DELEGATE_REGISTRATION(2_500_000_000L),
6+
VALIDATOR_REGISTRATION(2_500_000_000L),
77
VOTE(100_000_000L),
88
MULTI_SIGNATURE_REGISTRATION(500_000_000L),
99
IPFS(500_000_000L),
1010
MULTI_PAYMENT(10_000_000L),
11-
DELEGATE_RESIGNATION(2_500_000_000L),
11+
VALIDATOR_RESIGNATION(2_500_000_000L),
1212
HTLC_LOCK(10_000_000L),
1313
HTLC_CLAIM(0L),
1414
HTLC_REFUND(0L);

src/main/java/org/arkecosystem/crypto/enums/HtlcLockExpirationType.java

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/main/java/org/arkecosystem/crypto/identities/Address.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,4 @@ public static String fromPrivateKey(ECKey privateKey) {
4040
byte[] publicKeyBytes = privateKey.getPubKey();
4141
return fromPublicKey(Hex.encode(publicKeyBytes));
4242
}
43-
44-
public static Boolean validate(String address) {
45-
if (address == null || !address.matches("^0x[a-fA-F0-9]{40}$")) {
46-
return false;
47-
}
48-
return address.equals(Keys.toChecksumAddress(address));
49-
}
5043
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.arkecosystem.crypto.signature;
2+
3+
import java.util.Arrays;
4+
import org.bitcoinj.secp256k1.api.CompressedPubKeyData;
5+
6+
public class CompressedPubKeyDataImpl implements CompressedPubKeyData {
7+
8+
private final byte[] bytes;
9+
10+
public CompressedPubKeyDataImpl(byte[] bytes) {
11+
if (bytes.length != 33) {
12+
throw new IllegalArgumentException("Compressed public key must be 33 bytes");
13+
}
14+
this.bytes = Arrays.copyOf(bytes, bytes.length);
15+
}
16+
17+
@Override
18+
public byte[] bytes() {
19+
return Arrays.copyOf(bytes, bytes.length);
20+
}
21+
}

0 commit comments

Comments
 (0)