Skip to content

Commit b99d2a9

Browse files
committed
feat: add legacy address handling
1 parent e1394ce commit b99d2a9

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.arkecosystem.crypto.identities;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import org.arkecosystem.crypto.encoding.Base58;
5+
import org.arkecosystem.crypto.encoding.Hex;
6+
import org.bitcoinj.core.ECKey;
7+
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
8+
9+
public class LegacyAddress {
10+
public static String fromPassphrase(String passphrase, int pubKeyHash) {
11+
ECKey privateKey = PrivateKey.fromPassphrase(passphrase);
12+
return fromPrivateKey(privateKey, pubKeyHash);
13+
}
14+
15+
public static String fromPublicKey(String publicKey, int pubKeyHash) {
16+
byte[] ripemd160 = ripemd160(Hex.decode(publicKey));
17+
18+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
19+
outputStream.write(pubKeyHash);
20+
outputStream.write(ripemd160, 0, ripemd160.length);
21+
22+
return Base58.encodeChecked(outputStream.toByteArray());
23+
}
24+
25+
public static String fromPrivateKey(ECKey privateKey, int pubKeyHash) {
26+
return fromPublicKey(Hex.encode(privateKey.getPubKey()), pubKeyHash);
27+
}
28+
29+
public static boolean validate(String address, int pubKeyHash) {
30+
try {
31+
byte[] decoded = Base58.decodeChecked(address);
32+
33+
if (decoded.length != 21) {
34+
return false;
35+
}
36+
37+
return (decoded[0] & 0xFF) == pubKeyHash;
38+
} catch (Exception e) {
39+
return false;
40+
}
41+
}
42+
43+
private static byte[] ripemd160(byte[] input) {
44+
RIPEMD160Digest digest = new RIPEMD160Digest();
45+
digest.update(input, 0, input.length);
46+
byte[] output = new byte[digest.getDigestSize()];
47+
digest.doFinal(output, 0);
48+
return output;
49+
}
50+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.arkecosystem.crypto.identities;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import org.bitcoinj.core.ECKey;
8+
import org.junit.jupiter.api.Test;
9+
10+
public class LegacyAddressTest {
11+
12+
private static final int PUB_KEY_HASH = 30;
13+
private static final String PASSPHRASE =
14+
"enact busy minimum fantasy endless shoot reduce few inject ostrich snow promote";
15+
private static final String PUBLIC_KEY =
16+
"02a7c5ca78f6abbced169cb883aec3ffc0a0950affc0de575fb211873b5846e668";
17+
private static final String PRIVATE_KEY =
18+
"c7a0df6e1c42268946af49af28c49c6da64419f0203fa970b6e9be9f85a44875";
19+
private static final String ADDRESS = "D6WFwqYDRiFkSf4ezzWRt3jCsUp2sRmDMi";
20+
21+
@Test
22+
public void fromPassphrase() {
23+
assertEquals(ADDRESS, LegacyAddress.fromPassphrase(PASSPHRASE, PUB_KEY_HASH));
24+
}
25+
26+
@Test
27+
public void fromPublicKey() {
28+
assertEquals(ADDRESS, LegacyAddress.fromPublicKey(PUBLIC_KEY, PUB_KEY_HASH));
29+
}
30+
31+
@Test
32+
public void fromPrivateKey() {
33+
ECKey privateKey = PrivateKey.fromHex(PRIVATE_KEY);
34+
assertEquals(ADDRESS, LegacyAddress.fromPrivateKey(privateKey, PUB_KEY_HASH));
35+
}
36+
37+
@Test
38+
public void validate() {
39+
assertTrue(LegacyAddress.validate(ADDRESS, PUB_KEY_HASH));
40+
}
41+
42+
@Test
43+
public void validateFailsWithIncorrectPubKeyHash() {
44+
assertFalse(LegacyAddress.validate(ADDRESS, 32));
45+
}
46+
47+
@Test
48+
public void validateFailsWithInvalidAddress() {
49+
assertFalse(LegacyAddress.validate("D2WFnqYDRiFkSf4ezzWRt3jCsUp2sRmDMifwd", PUB_KEY_HASH));
50+
}
51+
52+
@Test
53+
public void validateFailsWithDecodingError() {
54+
assertFalse(LegacyAddress.validate("invalid", PUB_KEY_HASH));
55+
}
56+
}

0 commit comments

Comments
 (0)