Skip to content

Commit

Permalink
feat: keccak address support (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
alfonsobries authored Aug 5, 2024
1 parent 093dad1 commit 281aae5
Show file tree
Hide file tree
Showing 13 changed files with 42 additions and 53 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ repositories {
}

dependencies {
implementation 'org.web3j:core:4.8.7'
implementation 'org.bitcoinj:bitcoinj-core:0.16.3'
implementation 'com.google.code.gson:gson:2.11.0'
implementation 'com.google.guava:guava:30.2.0-jre'
Expand Down
62 changes: 27 additions & 35 deletions src/main/java/org/arkecosystem/crypto/identities/Address.java
Original file line number Diff line number Diff line change
@@ -1,58 +1,50 @@
package org.arkecosystem.crypto.identities;

import com.google.common.primitives.Bytes;
import org.arkecosystem.crypto.configuration.Network;
import org.arkecosystem.crypto.encoding.Base58;
import org.arkecosystem.crypto.encoding.Hex;
import org.bitcoinj.core.ECKey;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;

public class Address {
public static String fromPassphrase(String passphrase, Integer networkVersion) {
return fromPrivateKey(PrivateKey.fromPassphrase(passphrase), networkVersion);
}

public static String fromPassphrase(String passphrase) {
return Address.fromPassphrase(passphrase, null);
ECKey privateKey = PrivateKey.fromPassphrase(passphrase);
return fromPrivateKey(privateKey);
}

public static String fromPublicKey(String publicKey, Integer networkVersion) {
public static String fromPublicKey(String publicKey) {
byte[] publicKeyBytes = Hex.decode(publicKey);

RIPEMD160Digest digest = new RIPEMD160Digest();
digest.update(publicKeyBytes, 0, publicKeyBytes.length);
byte[] out = new byte[20];
digest.doFinal(out, 0);
// Ensure the public key is uncompressed
ECKey ecKey = ECKey.fromPublicOnly(publicKeyBytes);
byte[] uncompressedPublicKeyBytes = ecKey.getPubKeyPoint().getEncoded(false);

if (networkVersion == null) {
networkVersion = Network.get().version();
}
// Remove the prefix (0x04)
byte[] rawPublicKey = new byte[uncompressedPublicKeyBytes.length - 1];
System.arraycopy(uncompressedPublicKeyBytes, 1, rawPublicKey, 0, rawPublicKey.length);

byte[] bytes = Bytes.concat(new byte[] {networkVersion.byteValue()}, out);
return Base58.encodeChecked(bytes);
}
// Hash the public key using Keccak-256
byte[] keccakHash = Hash.sha3(rawPublicKey);

public static String fromPublicKey(String publicKey) {
return Address.fromPublicKey(publicKey, null);
}
// Take the last 20 bytes of the Keccak-256 hash
byte[] addressBytes = new byte[20];

public static String fromPrivateKey(ECKey privateKey, Integer networkVersion) {
return fromPublicKey(privateKey.getPublicKeyAsHex(), networkVersion);
}
System.arraycopy(keccakHash, keccakHash.length - 20, addressBytes, 0, 20);

public static String fromPrivateKey(ECKey privateKey) {
return Address.fromPrivateKey(privateKey, null);
}
// Convert to checksum address
String address = "0x" + Hex.encode(addressBytes);

public static Boolean validate(String address, Integer networkVersion) {
if (networkVersion == null) {
networkVersion = Network.get().version();
}
return Keys.toChecksumAddress(address);
}

return Base58.decodeChecked(address)[0] == networkVersion;
public static String fromPrivateKey(ECKey privateKey) {
byte[] publicKeyBytes = privateKey.getPubKey();
return fromPublicKey(Hex.encode(publicKeyBytes));
}

public static Boolean validate(String address) {
return Address.validate(address, null);
if (address == null || !address.matches("^0x[a-fA-F0-9]{40}$")) {
return false;
}
return address.equals(Keys.toChecksumAddress(address));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public VoteBuilder addVote(String vote) {
}

public VoteBuilder sign(String passphrase) {
this.transaction.recipientId = Address.fromPassphrase(passphrase, this.transaction.network);
this.transaction.recipientId = Address.fromPassphrase(passphrase);

super.sign(passphrase);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,34 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.arkecosystem.crypto.configuration.Network;
import org.arkecosystem.crypto.networks.Devnet;
import org.bitcoinj.core.ECKey;
import org.junit.jupiter.api.Test;

public class AddressTest {

@Test
public void fromPassphrase() {
Network.set(new Devnet());
String actual = Address.fromPassphrase("this is a top secret passphrase");
assertEquals("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", actual);
assertEquals("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01", actual);
}

@Test
public void fromPublicKey() {
String actual =
Address.fromPublicKey(
"034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192");
assertEquals("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", actual);
assertEquals("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01", actual);
}

@Test
public void fromPrivateKey() {
ECKey privateKey = PrivateKey.fromPassphrase("this is a top secret passphrase");
String actual = Address.fromPrivateKey(privateKey);
assertEquals("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", actual);
assertEquals("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01", actual);
}

@Test
public void validate() {
Network.set(new Devnet());
assertTrue(Address.validate("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"));
assertTrue(Address.validate("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01"));
}
}
2 changes: 1 addition & 1 deletion src/test/resources/identity.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712",
"publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
"address": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"address": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"wif": "SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA"
},
"passphrase": "this is a top secret passphrase"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 0,
"amount": 200000000,
"fee": 10000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"timestamp": 41443847,
"asset": {},
"vendorFieldHex": "48656c6c6f20576f726c64",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 0,
"amount": 200000000,
"fee": 10000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"timestamp": 41443847,
"asset": {},
"vendorField": "Hello World",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 0,
"amount": 200000000,
"fee": 10000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"timestamp": 41268326,
"asset": {},
"senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 0,
"amount": 200000000,
"fee": 10000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"timestamp": 41443865,
"asset": {},
"vendorFieldHex": "48656c6c6f20576f726c64",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 0,
"amount": 200000000,
"fee": 10000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"timestamp": 41443865,
"asset": {},
"vendorField": "Hello World",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 0,
"amount": 200000000,
"fee": 10000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"timestamp": 41268430,
"asset": {},
"senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/transactions/V1/vote/passphrase.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 3,
"amount": 0,
"fee": 100000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
"timestamp": 41269349,
"asset": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": 3,
"amount": 0,
"fee": 100000000,
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
"timestamp": 41269366,
"asset": {
Expand Down

0 comments on commit 281aae5

Please sign in to comment.