Skip to content

Commit 281aae5

Browse files
authored
feat: keccak address support (#179)
1 parent 093dad1 commit 281aae5

File tree

13 files changed

+42
-53
lines changed

13 files changed

+42
-53
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ repositories {
2222
}
2323

2424
dependencies {
25+
implementation 'org.web3j:core:4.8.7'
2526
implementation 'org.bitcoinj:bitcoinj-core:0.16.3'
2627
implementation 'com.google.code.gson:gson:2.11.0'
2728
implementation 'com.google.guava:guava:30.2.0-jre'
Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,50 @@
11
package org.arkecosystem.crypto.identities;
22

3-
import com.google.common.primitives.Bytes;
4-
import org.arkecosystem.crypto.configuration.Network;
5-
import org.arkecosystem.crypto.encoding.Base58;
63
import org.arkecosystem.crypto.encoding.Hex;
74
import org.bitcoinj.core.ECKey;
8-
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
5+
import org.web3j.crypto.Hash;
6+
import org.web3j.crypto.Keys;
97

108
public class Address {
11-
public static String fromPassphrase(String passphrase, Integer networkVersion) {
12-
return fromPrivateKey(PrivateKey.fromPassphrase(passphrase), networkVersion);
13-
}
14-
159
public static String fromPassphrase(String passphrase) {
16-
return Address.fromPassphrase(passphrase, null);
10+
ECKey privateKey = PrivateKey.fromPassphrase(passphrase);
11+
return fromPrivateKey(privateKey);
1712
}
1813

19-
public static String fromPublicKey(String publicKey, Integer networkVersion) {
14+
public static String fromPublicKey(String publicKey) {
2015
byte[] publicKeyBytes = Hex.decode(publicKey);
2116

22-
RIPEMD160Digest digest = new RIPEMD160Digest();
23-
digest.update(publicKeyBytes, 0, publicKeyBytes.length);
24-
byte[] out = new byte[20];
25-
digest.doFinal(out, 0);
17+
// Ensure the public key is uncompressed
18+
ECKey ecKey = ECKey.fromPublicOnly(publicKeyBytes);
19+
byte[] uncompressedPublicKeyBytes = ecKey.getPubKeyPoint().getEncoded(false);
2620

27-
if (networkVersion == null) {
28-
networkVersion = Network.get().version();
29-
}
21+
// Remove the prefix (0x04)
22+
byte[] rawPublicKey = new byte[uncompressedPublicKeyBytes.length - 1];
23+
System.arraycopy(uncompressedPublicKeyBytes, 1, rawPublicKey, 0, rawPublicKey.length);
3024

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

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

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

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

47-
public static Boolean validate(String address, Integer networkVersion) {
48-
if (networkVersion == null) {
49-
networkVersion = Network.get().version();
50-
}
36+
return Keys.toChecksumAddress(address);
37+
}
5138

52-
return Base58.decodeChecked(address)[0] == networkVersion;
39+
public static String fromPrivateKey(ECKey privateKey) {
40+
byte[] publicKeyBytes = privateKey.getPubKey();
41+
return fromPublicKey(Hex.encode(publicKeyBytes));
5342
}
5443

5544
public static Boolean validate(String address) {
56-
return Address.validate(address, null);
45+
if (address == null || !address.matches("^0x[a-fA-F0-9]{40}$")) {
46+
return false;
47+
}
48+
return address.equals(Keys.toChecksumAddress(address));
5749
}
5850
}

src/main/java/org/arkecosystem/crypto/transactions/builder/VoteBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public VoteBuilder addVote(String vote) {
2424
}
2525

2626
public VoteBuilder sign(String passphrase) {
27-
this.transaction.recipientId = Address.fromPassphrase(passphrase, this.transaction.network);
27+
this.transaction.recipientId = Address.fromPassphrase(passphrase);
2828

2929
super.sign(passphrase);
3030

src/test/java/org/arkecosystem/crypto/identities/AddressTest.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,34 @@
33
import static org.junit.jupiter.api.Assertions.assertEquals;
44
import static org.junit.jupiter.api.Assertions.assertTrue;
55

6-
import org.arkecosystem.crypto.configuration.Network;
7-
import org.arkecosystem.crypto.networks.Devnet;
86
import org.bitcoinj.core.ECKey;
97
import org.junit.jupiter.api.Test;
108

119
public class AddressTest {
1210

1311
@Test
1412
public void fromPassphrase() {
15-
Network.set(new Devnet());
1613
String actual = Address.fromPassphrase("this is a top secret passphrase");
17-
assertEquals("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", actual);
14+
assertEquals("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01", actual);
1815
}
1916

2017
@Test
2118
public void fromPublicKey() {
2219
String actual =
2320
Address.fromPublicKey(
2421
"034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192");
25-
assertEquals("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", actual);
22+
assertEquals("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01", actual);
2623
}
2724

2825
@Test
2926
public void fromPrivateKey() {
3027
ECKey privateKey = PrivateKey.fromPassphrase("this is a top secret passphrase");
3128
String actual = Address.fromPrivateKey(privateKey);
32-
assertEquals("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", actual);
29+
assertEquals("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01", actual);
3330
}
3431

3532
@Test
3633
public void validate() {
37-
Network.set(new Devnet());
38-
assertTrue(Address.validate("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"));
34+
assertTrue(Address.validate("0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01"));
3935
}
4036
}

src/test/resources/identity.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"data": {
33
"privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712",
44
"publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
5-
"address": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
5+
"address": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
66
"wif": "SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA"
77
},
88
"passphrase": "this is a top secret passphrase"

src/test/resources/transactions/V1/transfer/passphrase-with-vendor-field-hex.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": 0,
44
"amount": 200000000,
55
"fee": 10000000,
6-
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
6+
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
77
"timestamp": 41443847,
88
"asset": {},
99
"vendorFieldHex": "48656c6c6f20576f726c64",

src/test/resources/transactions/V1/transfer/passphrase-with-vendor-field.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": 0,
44
"amount": 200000000,
55
"fee": 10000000,
6-
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
6+
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
77
"timestamp": 41443847,
88
"asset": {},
99
"vendorField": "Hello World",

src/test/resources/transactions/V1/transfer/passphrase.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": 0,
44
"amount": 200000000,
55
"fee": 10000000,
6-
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
6+
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
77
"timestamp": 41268326,
88
"asset": {},
99
"senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",

src/test/resources/transactions/V1/transfer/second-passphrase-with-vendor-field-hex.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": 0,
44
"amount": 200000000,
55
"fee": 10000000,
6-
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
6+
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
77
"timestamp": 41443865,
88
"asset": {},
99
"vendorFieldHex": "48656c6c6f20576f726c64",

src/test/resources/transactions/V1/transfer/second-passphrase-with-vendor-field.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": 0,
44
"amount": 200000000,
55
"fee": 10000000,
6-
"recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
6+
"recipientId": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
77
"timestamp": 41443865,
88
"asset": {},
99
"vendorField": "Hello World",

0 commit comments

Comments
 (0)