diff --git a/src/main/java/org/arkecosystem/crypto/identities/LegacyAddress.java b/src/main/java/org/arkecosystem/crypto/identities/LegacyAddress.java new file mode 100644 index 0000000..fbe8d07 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/identities/LegacyAddress.java @@ -0,0 +1,50 @@ +package org.arkecosystem.crypto.identities; + +import java.io.ByteArrayOutputStream; +import org.arkecosystem.crypto.encoding.Base58; +import org.arkecosystem.crypto.encoding.Hex; +import org.bitcoinj.core.ECKey; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; + +public class LegacyAddress { + public static String fromPassphrase(String passphrase, int pubKeyHash) { + ECKey privateKey = PrivateKey.fromPassphrase(passphrase); + return fromPrivateKey(privateKey, pubKeyHash); + } + + public static String fromPublicKey(String publicKey, int pubKeyHash) { + byte[] ripemd160 = ripemd160(Hex.decode(publicKey)); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(pubKeyHash); + outputStream.write(ripemd160, 0, ripemd160.length); + + return Base58.encodeChecked(outputStream.toByteArray()); + } + + public static String fromPrivateKey(ECKey privateKey, int pubKeyHash) { + return fromPublicKey(Hex.encode(privateKey.getPubKey()), pubKeyHash); + } + + public static boolean validate(String address, int pubKeyHash) { + try { + byte[] decoded = Base58.decodeChecked(address); + + if (decoded.length != 21) { + return false; + } + + return (decoded[0] & 0xFF) == pubKeyHash; + } catch (Exception e) { + return false; + } + } + + private static byte[] ripemd160(byte[] input) { + RIPEMD160Digest digest = new RIPEMD160Digest(); + digest.update(input, 0, input.length); + byte[] output = new byte[digest.getDigestSize()]; + digest.doFinal(output, 0); + return output; + } +} diff --git a/src/test/java/org/arkecosystem/crypto/identities/LegacyAddressTest.java b/src/test/java/org/arkecosystem/crypto/identities/LegacyAddressTest.java new file mode 100644 index 0000000..5aeeca5 --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/identities/LegacyAddressTest.java @@ -0,0 +1,56 @@ +package org.arkecosystem.crypto.identities; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.bitcoinj.core.ECKey; +import org.junit.jupiter.api.Test; + +public class LegacyAddressTest { + + private static final int PUB_KEY_HASH = 30; + private static final String PASSPHRASE = + "enact busy minimum fantasy endless shoot reduce few inject ostrich snow promote"; + private static final String PUBLIC_KEY = + "02a7c5ca78f6abbced169cb883aec3ffc0a0950affc0de575fb211873b5846e668"; + private static final String PRIVATE_KEY = + "c7a0df6e1c42268946af49af28c49c6da64419f0203fa970b6e9be9f85a44875"; + private static final String ADDRESS = "D6WFwqYDRiFkSf4ezzWRt3jCsUp2sRmDMi"; + + @Test + public void fromPassphrase() { + assertEquals(ADDRESS, LegacyAddress.fromPassphrase(PASSPHRASE, PUB_KEY_HASH)); + } + + @Test + public void fromPublicKey() { + assertEquals(ADDRESS, LegacyAddress.fromPublicKey(PUBLIC_KEY, PUB_KEY_HASH)); + } + + @Test + public void fromPrivateKey() { + ECKey privateKey = PrivateKey.fromHex(PRIVATE_KEY); + assertEquals(ADDRESS, LegacyAddress.fromPrivateKey(privateKey, PUB_KEY_HASH)); + } + + @Test + public void validate() { + assertTrue(LegacyAddress.validate(ADDRESS, PUB_KEY_HASH)); + } + + @Test + public void validateFailsWithIncorrectPubKeyHash() { + assertFalse(LegacyAddress.validate(ADDRESS, 32)); + } + + @Test + public void validateFailsWithInvalidAddress() { + assertFalse(LegacyAddress.validate("D2WFnqYDRiFkSf4ezzWRt3jCsUp2sRmDMifwd", PUB_KEY_HASH)); + } + + @Test + public void validateFailsWithDecodingError() { + assertFalse(LegacyAddress.validate("invalid", PUB_KEY_HASH)); + } +}