-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
152 additions
and
1 deletion.
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
blockchain/src/main/java/com/tangem/blockchain/blockchains/casper/CasperAddressService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.tangem.blockchain.blockchains.casper | ||
|
||
import com.tangem.blockchain.blockchains.casper.cashaddr.CasperCashAddr.checksum | ||
import com.tangem.blockchain.common.address.AddressService | ||
import com.tangem.common.card.EllipticCurve | ||
import com.tangem.common.extensions.hexToBytes | ||
|
||
class CasperAddressService : AddressService() { | ||
override fun makeAddress(walletPublicKey: ByteArray, curve: EllipticCurve?): String { | ||
val prefix = CasperConstants.getAddressPrefix(curve!!) | ||
val bytes = prefix.hexToBytes() + walletPublicKey | ||
return bytes.checksum() | ||
} | ||
|
||
override fun validate(address: String): Boolean { | ||
val isCorrectEd25519Address = address.length == CasperConstants.ED25519_LENGTH && | ||
address.startsWith(CasperConstants.ED25519_PREFIX) | ||
val isCorrectSecp256k1Address = address.length == CasperConstants.SECP256K1_LENGTH && | ||
address.startsWith(CasperConstants.SECP256K1_PREFIX) | ||
|
||
if (!isCorrectEd25519Address && !isCorrectSecp256k1Address) { | ||
return false | ||
} | ||
|
||
val addressChecksum = address.hexToBytes().checksum() | ||
return addressChecksum == addressChecksum.hexToBytes().checksum() | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
blockchain/src/main/java/com/tangem/blockchain/blockchains/casper/CasperConstants.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.tangem.blockchain.blockchains.casper | ||
|
||
import com.tangem.common.card.EllipticCurve | ||
|
||
object CasperConstants { | ||
const val ED25519_PREFIX = "01" | ||
const val ED25519_LENGTH = 66 | ||
|
||
const val SECP256K1_PREFIX = "02" | ||
const val SECP256K1_LENGTH = 68 | ||
|
||
fun getAddressPrefix(curve: EllipticCurve) = when (curve) { | ||
EllipticCurve.Ed25519, | ||
EllipticCurve.Ed25519Slip0010, | ||
-> ED25519_PREFIX | ||
EllipticCurve.Secp256k1, | ||
-> SECP256K1_PREFIX | ||
// added as unsupported for now, need to research | ||
EllipticCurve.Secp256r1, | ||
EllipticCurve.Bls12381G2, | ||
EllipticCurve.Bls12381G2Aug, | ||
EllipticCurve.Bls12381G2Pop, | ||
EllipticCurve.Bip0340, | ||
-> error("${curve.curve} is not supported") | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
blockchain/src/main/java/com/tangem/blockchain/blockchains/casper/cashaddr/CasperCashAddr.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.tangem.blockchain.blockchains.casper.cashaddr | ||
|
||
import org.bouncycastle.jcajce.provider.digest.Blake2b | ||
|
||
/** | ||
* Casper checksummed hex encoding following an [EIP-55][1]-like scheme | ||
* | ||
* @see <a href="https://github.com/casper-ecosystem/casper-js-sdk/blob/dev/src/lib/ChecksummedHex.ts">Source</a> | ||
*/ | ||
object CasperCashAddr { | ||
fun ByteArray.checksum(): String = encode(sliceArray(0..0)) + encode(sliceArray(1 until size)) | ||
|
||
// Separate bytes inside ByteArray to nibbles | ||
// E.g. [0x01, 0x55, 0xFF] -> [0x00, 0x01, 0x50, 0x05, 0xF0, 0x0F] | ||
@Suppress("MagicNumber") | ||
private fun bytesToNibbles(bytes: ByteArray): ByteArray = bytes | ||
.flatMap { byte -> | ||
listOf( | ||
(byte.toInt() and 0xff shr 4).toByte(), | ||
(byte.toInt() and 0x0f).toByte(), | ||
) | ||
}.toByteArray() | ||
|
||
// Separate bytes inside ByteArray to bits array | ||
// E.g. [0x01] -> [false, false, false, false, false, false, false, true] | ||
// E.g. [0xAA] -> [true, false, true, false, true, false, true, false] | ||
@Suppress("MagicNumber") | ||
private fun bytesToBitsCycle(bytes: ByteArray): BooleanArray = bytes | ||
.flatMap { byte -> | ||
List(8) { i -> | ||
byte.toInt() shr i and 0x01 == 0x01 | ||
} | ||
}.toBooleanArray() | ||
|
||
private fun byteHash(bytes: ByteArray): ByteArray = Blake2b.Blake2b256().digest(bytes) | ||
|
||
private fun encode(input: ByteArray): String { | ||
val inputNibbles = bytesToNibbles(input) | ||
val hashBits = bytesToBitsCycle(byteHash(input)) | ||
val hashBitsValues = hashBits.iterator() | ||
val hexOutputString = inputNibbles.fold(StringBuilder()) { accum, nibble -> | ||
val c = "%x".format(nibble) | ||
|
||
if (Regex("^[a-zA-Z()]+\$").matches(c) && hashBitsValues.hasNext() && hashBitsValues.next()) { | ||
accum.append(c.uppercase()) | ||
} else { | ||
accum.append(c.lowercase()) | ||
} | ||
accum | ||
}.toString() | ||
return hexOutputString | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
blockchain/src/test/java/com/tangem/blockchain/blockchains/casper/CasperAddressTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.tangem.blockchain.blockchains.casper | ||
|
||
import com.google.common.truth.Truth | ||
import com.tangem.common.card.EllipticCurve | ||
import com.tangem.common.extensions.hexToBytes | ||
import org.junit.Test | ||
|
||
class CasperAddressTest { | ||
|
||
private val addressService = CasperAddressService() | ||
|
||
@Test | ||
fun makeAddressFromCorrectEd25519PublicKey() { | ||
val walletPublicKey = "98C07D7E72D89A681D7227A7AF8A6FD5F22FE0105C8741D55A95DF415454B82E".hexToBytes() | ||
val expected = "0198c07D7e72D89A681d7227a7Af8A6fd5F22fe0105c8741d55A95dF415454b82E" | ||
|
||
Truth.assertThat(addressService.makeAddress(walletPublicKey, EllipticCurve.Ed25519)).isEqualTo(expected) | ||
} | ||
|
||
@Test | ||
fun validateCorrectEd25519Address() { | ||
val address = "0198c07D7e72D89A681d7227a7Af8A6fd5F22fe0105c8741d55A95dF415454b82E" | ||
|
||
Truth.assertThat(addressService.validate(address)).isTrue() | ||
} | ||
|
||
@Test | ||
fun makeAddressFromCorrectSecp256k1PublicKey() { | ||
val walletPublicKey = "021F997DFBBFD32817C0E110EAEE26BCBD2BB70B4640C515D9721C9664312EACD8".hexToBytes() | ||
val expected = "02021f997DfbbFd32817C0E110EAeE26BCbD2BB70b4640C515D9721c9664312eaCd8" | ||
|
||
Truth.assertThat(addressService.makeAddress(walletPublicKey, EllipticCurve.Secp256k1)).isEqualTo(expected) | ||
} | ||
|
||
@Test | ||
fun validateCorrectSecp256k1Address() { | ||
val address = "02021f997DfbbFd32817C0E110EAeE26BCbD2BB70b4640C515D9721c9664312eaCd8" | ||
|
||
Truth.assertThat(addressService.validate(address)).isTrue() | ||
} | ||
} |