Skip to content

Commit

Permalink
New Account passes basic tests
Browse files Browse the repository at this point in the history
  • Loading branch information
arietrouw committed Nov 14, 2024
1 parent 3803c0f commit 7cf6689
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 28 deletions.
45 changes: 45 additions & 0 deletions sdk/src/androidTest/java/network/xyo/client/account/AccountTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package network.xyo.client.account

import android.util.Log
import org.junit.Test

class AccountTest {

val testVectorPrivateKey = "7f71bc5644f8f521f7e9b73f7a391e82c05432f8a9d36c44d6b1edbf1d8db62f"
val testVectorPublicKey
= "04ed6f3b86542f45aab88ec48ab1366b462bd993fec83e234054afd8f2311fba774800fdb40c04918463b463a6044b83413a604550bfba8f8911beb65475d6528e"
val testVectorAddress = "5e7a847447e7fec41011ae7d32d768f86605ba03"
val testVectorHash = "4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a"
val testVectorSignature
= "b61dad551e910e2793b4f9f880125b5799086510ce102fad0222c1b093c60a6b38aa35ef56f97f86537269e8be95832aaa37d3b64d86b67f0cda467ac7cb5b3e"

@OptIn(ExperimentalStdlibApi::class)
@Test
fun testRandomAccount() {
val account = Account.random()
Log.i("account.privateKey", account.privateKey.toHexString())
Log.i("account.privateKey.count()", account.privateKey.count().toString())
assert(account.privateKey.count() == 32)
Log.i("account.publicKey", account.publicKey.toHexString())
Log.i("account.publicKey.count()", account.publicKey.count().toString())
assert(account.publicKey.count() == 65)
}

@OptIn(ExperimentalStdlibApi::class)
@Test
fun testKnownPrivateKeyAccount() {
val account = Account.fromPrivateKey(hexStringToByteArray(testVectorPrivateKey))
assert(account.privateKey.count() == 32)
assert(account.publicKey.count() == 65)
Log.i("publicKey", account.publicKey.toHexString())
Log.i("publicKeyVector", testVectorPublicKey)
Log.i("address", account.address.toHexString())
Log.i("addressVector", testVectorAddress)
assert(account.address.toHexString() == testVectorAddress)
val signature = account.sign(hexStringToByteArray(testVectorHash))
Log.i("signature", signature.toHexString())
Log.i("signatureVector", testVectorSignature)
assert(signature.toHexString() == testVectorSignature)
assert(account.verify(hexStringToByteArray(testVectorHash), signature))
}
}
76 changes: 58 additions & 18 deletions sdk/src/main/java/network/xyo/client/account/Account.kt
Original file line number Diff line number Diff line change
@@ -1,52 +1,92 @@
package network.xyo.client.account

import network.xyo.client.XyoSerializable
import network.xyo.client.account.model.AccountInstance
import network.xyo.client.account.model.AccountStatic
import network.xyo.client.account.model.PreviousHashStore
import network.xyo.client.address.XyoData.Companion.copyByteArrayWithLeadingPaddingOrTrim
import network.xyo.client.address.XyoEllipticKey.Companion.CURVE
import org.spongycastle.crypto.digests.SHA256Digest
import org.spongycastle.crypto.params.ECPrivateKeyParameters
import org.spongycastle.crypto.signers.ECDSASigner
import org.spongycastle.crypto.signers.HMacDSAKCalculator
import org.spongycastle.jcajce.provider.digest.Keccak
import tech.figure.hdwallet.ec.PrivateKey
import tech.figure.hdwallet.ec.extensions.toBytesPadded
import tech.figure.hdwallet.ec.secp256k1Curve
import tech.figure.hdwallet.signer.ASN1Signature
import tech.figure.hdwallet.signer.BCECSigner
import tech.figure.hdwallet.signer.BTCSignature
import tech.figure.hdwallet.signer.ECDSASignature
import tech.figure.hdwallet.wallet.Account as TFAccount
import java.math.BigInteger
import java.security.SecureRandom

open class Account(private val _account: TFAccount, private var _previousHash: ByteArray? = null): AccountInstance {
open class Account(private val _privateKey: PrivateKey, private var _previousHash: ByteArray? = null): AccountInstance {

constructor(privateKey: PrivateKey, previousHash: ByteArray? = null) : this(TFAccount.fromBip32("", toBase58(privateKey.key)), previousHash) {}
constructor(privateKey: ByteArray, previousHash: ByteArray? = null) : this(TFAccount.fromBip32("", toBase58(privateKey)), previousHash) {}
constructor(privateKey: ByteArray, previousHash: ByteArray? = null) : this(PrivateKey.fromBytes(privateKey, secp256k1Curve), previousHash) {}
constructor(privateKey: BigInteger, previousHash: ByteArray? = null) : this(privateKey.toByteArray(), previousHash) {}

private val _address = hexStringToByteArray(_account.keyPair.publicKey.address("").value)
private val _address = addressFromPublicKey(publicKey)

override val address: ByteArray
final override val address: ByteArray
get() = _address
override val previousHash: ByteArray?
final override val previousHash: ByteArray?
get() = _previousHash
override val privateKey: ByteArray
get() = _account.keyPair.privateKey.key.toByteArray()
override val publicKey: ByteArray
get() = _account.keyPair.publicKey.key.toByteArray()
final override val privateKey: ByteArray
get() = _privateKey.key.toBytesPadded(32)
final override val publicKey: ByteArray
get() = publicKeyFromPrivateKey(BigInteger(privateKey))

override fun sign(hash: ByteArray): ByteArray {
val result = BCECSigner().sign(_account.keyPair.privateKey, hash)
val result = BCECSigner().sign(_privateKey, hash)
_previousHash = hash
return result.encodeAsASN1DER().toByteArray()
return result.encodeAsBTC().toByteArray()
}

override fun verify(msg: ByteArray, signature: ByteArray): Boolean {
return BCECSigner().verify(_account.keyPair.publicKey, msg, ECDSASignature.Companion.decode(
ASN1Signature.fromByteArray(signature)))
return BCECSigner().verify(_privateKey.toPublicKey(), msg, ECDSASignature.Companion.decode(
BTCSignature.fromByteArray(signature)))
}

companion object: AccountStatic<AccountInstance> {
override var previousHashStore: PreviousHashStore? = null

@OptIn(ExperimentalStdlibApi::class)
override fun fromPrivateKey(key: ByteArray): AccountInstance {
return Account(PrivateKey.fromBytes(key, secp256k1Curve))
return Account(key)
}

override fun random(): AccountInstance {
TODO("Not yet implemented")
return fromPrivateKey(generatePrivateKeyBytes())
}

fun addressFromPublicKey(key: ByteArray): ByteArray {
val publicKeyHash = toKeccak(key.copyOfRange(1, key.size))
return publicKeyHash.copyOfRange(12, publicKeyHash.size)
}

private fun toKeccak(bytes: ByteArray): ByteArray {
val keccak = Keccak.Digest256()
keccak.update(bytes)
return keccak.digest()
}

private fun generatePrivateKeyBytes(): ByteArray {
val secureRandom = SecureRandom()
val private = ByteArray(32)
secureRandom.nextBytes(private)
//this line is to make sure the key is below n
while(BigInteger(private) > secp256k1Curve.n) {
secureRandom.nextBytes(private)
}
return private
}

fun publicKeyFromPrivateKey(private: BigInteger): ByteArray {
return secp256k1Curve.g.mul(private).encoded(false)
}

fun bytesToBigInteger(bb: ByteArray): BigInteger {
return if (bb.isEmpty()) BigInteger.ZERO else BigInteger(1, bb)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package network.xyo.client.account.model

interface AccountInstance {
val address: ByteArray
interface AccountInstance: PrivateKeyInstance {
val previousHash: ByteArray?
val privateKey: ByteArray
val publicKey: ByteArray
fun sign(hash: ByteArray): ByteArray
fun verify(msg: ByteArray, signature: ByteArray): Boolean
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package network.xyo.client.account.model

interface PrivateKeyInstance: PublicKeyInstance {
fun sign(hash: ByteArray, previousHash: ByteArray?): ByteArray
fun sign(hash: ByteArray): ByteArray
}

0 comments on commit 7cf6689

Please sign in to comment.