Skip to content

Commit

Permalink
Adding MixedCaseChecksum feature implementing CEP57 standard
Browse files Browse the repository at this point in the history
  • Loading branch information
AB3rtz committed Sep 16, 2022
1 parent 7f4dd58 commit 769dcf3
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,60 @@
package com.syntifi.crypto.key.checksum;

import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.util.encoders.Hex;
import com.syntifi.crypto.key.encdec.Hex;
import com.syntifi.crypto.key.hash.Blake2b;
import com.syntifi.crypto.key.hash.Keccak256;

/**
* Implementation of the EIP-55: Mixed-case checksum address encoding
* Documentation available at: https://eips.ethereum.org/EIPS/eip-55
* Mixed checksumEncoding
*
* @author Alexandre Carvalho
* @author Andre Bertolace
* @since 0.5.0
*/
public class MixedCaseChecksum {

public static String kekkac256(String value) {
Keccak.DigestKeccak kekkac256 = new Keccak.Digest256();
byte[] hash = kekkac256.digest(value.getBytes());
return Hex.toHexString(hash);
}

public static String checksumEncode(String address) {
Boolean withPrefix = address.startsWith("0x");
String value = address.toLowerCase().replace("0x", "");
char[] hash = kekkac256(value).toCharArray();
/**
* Implementation of the EIP-55: Mixed-case checksum address encoding
* Documentation available at: https://eips.ethereum.org/EIPS/eip-55
*
* @param value Hex string
* @return
*/
public static String checksumEncodeEIP55(String value) {
char[] hash = Hex.encode(Keccak256.digest(value.toLowerCase().getBytes())).toCharArray();
char[] chars = value.toCharArray();
char[] encoded = new char[chars.length];
for (int i = 0; i < chars.length; i++) {
encoded[i] = Character.digit(hash[i], 16) > 7
? Character.toUpperCase(chars[i])
: chars[i];
: Character.toLowerCase(chars[i]);
}
return withPrefix
? "0x" + String.valueOf(encoded)
: String.valueOf(encoded);
return String.valueOf(encoded);
}

/**
* Implementation of the CEP-57
* Documentation available at: https://github.com/casper-network/ceps/blob/master/text/0057-checksummed-addresses.md
*
* @param value Hex string
* @return
*/
public static String checksumEncodeCEP57(String value) {
byte[] hash = Blake2b.digest(Hex.decode(value), 32);
char[] chars = value.toCharArray();
char[] encoded = new char[chars.length];
int whichByte = 0;
int whichBit = 0;
for (int i = 0; i < chars.length; i++) {
boolean bitSet = (Byte.toUnsignedInt(hash[whichByte]) & (1<<whichBit)) != 0;
if (Character.isAlphabetic(chars[i])) {
encoded[i] = bitSet ? Character.toUpperCase(chars[i]) : Character.toLowerCase(chars[i]);
whichByte = (whichByte + whichBit/7) % hash.length;
whichBit = (whichBit + 1) % 8;
} else {
encoded[i] = chars[i];
}
}
return String.valueOf(encoded);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.syntifi.crypto.key.hash;

import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.util.encoders.Hex;

public class Keccak256 {

public static byte[] digest(byte[] value) {
Keccak.DigestKeccak kekkac256 = new Keccak.Digest256();
byte[] hash = kekkac256.digest(value);
return hash;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,32 @@
public class MixedCaseChecksumTest {

@Test
void shouldMatchLowerCaseAddress() {
assertEquals("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
MixedCaseChecksum.checksumEncode("0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"));
void eip55ShouldMatchLowerCaseAddress() {
assertEquals("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
MixedCaseChecksum.checksumEncodeEIP55("fb6916095ca1df60bb79ce92ce3ea74c37c5d359"));
}

@Test
void shouldMatchUpperCaseAddress() {
assertEquals("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
MixedCaseChecksum.checksumEncode("0xFB6916095CA1DF60BB79CE92CE3EA74C37C5D359"));
void eip55ShouldMatchUpperCaseAddress() {
assertEquals("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
MixedCaseChecksum.checksumEncodeEIP55("FB6916095CA1DF60BB79CE92CE3EA74C37C5D359"));
}

@Test
void kekkac256ShouldMatch() {
assertEquals("a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
MixedCaseChecksum.kekkac256("transfer(address,uint256)"));
void cep57houldMatchEmptyAddress() {
assertEquals("",
MixedCaseChecksum.checksumEncodeEIP55(""));
}
@Test
void cep57houldMatchLowerCaseAddress() {
assertEquals("010573D52dFA032716fDC2AE5396987f280304106fC11f6Ac6Ccf287B81dc09ED7",
MixedCaseChecksum.checksumEncodeCEP57("010573d52dfa032716fdc2ae5396987f280304106fc11f6ac6ccf287b81dc09ed7"));
}

@Test
void cep57houldMatchUpperCaseAddress() {
assertEquals("51DA5aE5C39880Bfe4f94B0898332d1BD37e647F72f79Cf23475df1Bb1f85bEA",
MixedCaseChecksum.checksumEncodeCEP57("51da5ae5c39880bfe4f94b0898332d1bd37e647f72f79cf23475df1bb1f85bea"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.syntifi.crypto.key.hash;

import com.syntifi.crypto.key.encdec.Hex;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;

public class Blake2bTest {

@Test
void test() {
Assertions.assertEquals("44682ea86b704fb3c65cd16f84a76b621e04bbdb3746280f25cf062220e471b4",
Hex.encode(Blake2b.digest("أبو يوسف يعقوب بن إسحاق الصبّاح الكندي‎".getBytes(StandardCharsets.UTF_8), 32)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.syntifi.crypto.key.hash;

import com.syntifi.crypto.key.encdec.Hex;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class Keccak256Test {

@Test
void kekkac256ShouldMatch() {
Assertions.assertEquals("a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
Hex.encode(Keccak256.digest("transfer(address,uint256)".getBytes())));
}
}

0 comments on commit 769dcf3

Please sign in to comment.