parent
4c2fdeeb73
commit
e95e43b83b
|
@ -81,3 +81,4 @@ DecompileChecker.kt
|
||||||
backup-dbs/
|
backup-dbs/
|
||||||
*.db
|
*.db
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/.idea/androidTestResultsUserPreferences.xml
|
||||||
|
|
|
@ -287,8 +287,6 @@ dependencies {
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation(libs.kotlin.reflect)
|
testImplementation(libs.kotlin.reflect)
|
||||||
testImplementation(libs.kotlin.test)
|
testImplementation(libs.kotlin.test)
|
||||||
testImplementation(libs.mockito.junit)
|
|
||||||
testImplementation(libs.mockito.kotlin)
|
|
||||||
testImplementation(libs.bundles.junit)
|
testImplementation(libs.bundles.junit)
|
||||||
testImplementation(libs.grpc.testing)
|
testImplementation(libs.grpc.testing)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package cash.z.ecc.android.sdk.fixture
|
||||||
|
|
||||||
|
import cash.z.ecc.android.bip39.Mnemonics
|
||||||
|
import cash.z.ecc.android.sdk.model.Account
|
||||||
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||||
|
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||||
|
|
||||||
|
object WalletFixture {
|
||||||
|
val NETWORK = ZcashNetwork.Mainnet
|
||||||
|
const val SEED_PHRASE =
|
||||||
|
"kitchen renew wide common vague fold vacuum tilt amazing pear square gossip jewel month tree shock scan alpha just spot fluid toilet view dinner"
|
||||||
|
|
||||||
|
suspend fun getUnifiedSpendingKey(
|
||||||
|
seed: String = SEED_PHRASE,
|
||||||
|
network: ZcashNetwork = NETWORK,
|
||||||
|
account: Account = Account.DEFAULT
|
||||||
|
) = DerivationTool.deriveUnifiedSpendingKey(Mnemonics.MnemonicCode(seed).toEntropy(), network, account)
|
||||||
|
}
|
|
@ -28,8 +28,7 @@ class TransparentTest(val expected: Expected, val network: ZcashNetwork) {
|
||||||
expected.tAccountPrivKey,
|
expected.tAccountPrivKey,
|
||||||
DerivationTool.deriveTransparentAccountPrivateKey(
|
DerivationTool.deriveTransparentAccountPrivateKey(
|
||||||
SEED,
|
SEED,
|
||||||
network =
|
network = network,
|
||||||
network,
|
|
||||||
Account.DEFAULT
|
Account.DEFAULT
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package cash.z.ecc.android.sdk.model
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import cash.z.ecc.android.sdk.fixture.WalletFixture
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class UnifiedSpendingKeyTest {
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
fun factory_copies_bytes() = runTest {
|
||||||
|
val spendingKey = WalletFixture.getUnifiedSpendingKey()
|
||||||
|
val expected = spendingKey.copyBytes().copyOf()
|
||||||
|
|
||||||
|
val bytes = spendingKey.copyBytes()
|
||||||
|
val newSpendingKey = UnifiedSpendingKey.new(spendingKey.account, bytes)
|
||||||
|
bytes.clear()
|
||||||
|
|
||||||
|
assertContentEquals(expected, newSpendingKey.getOrThrow().copyBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
fun get_copies_bytes() = runTest {
|
||||||
|
val spendingKey = WalletFixture.getUnifiedSpendingKey()
|
||||||
|
|
||||||
|
val expected = spendingKey.copyBytes()
|
||||||
|
val newSpendingKey = UnifiedSpendingKey.new(spendingKey.account, expected)
|
||||||
|
|
||||||
|
newSpendingKey.getOrThrow().copyBytes().clear()
|
||||||
|
|
||||||
|
assertContentEquals(expected, newSpendingKey.getOrThrow().copyBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
fun toString_does_not_leak() = runTest {
|
||||||
|
assertEquals(
|
||||||
|
"UnifiedSpendingKey(account=Account(value=0))",
|
||||||
|
WalletFixture.getUnifiedSpendingKey().toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteArray.clear() {
|
||||||
|
for (i in indices) {
|
||||||
|
this[i] = 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package cash.z.ecc.android.sdk.tool
|
||||||
|
|
||||||
|
import cash.z.ecc.android.bip39.Mnemonics
|
||||||
|
import cash.z.ecc.android.sdk.fixture.WalletFixture
|
||||||
|
import cash.z.ecc.android.sdk.model.Account
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class DerivationToolTest {
|
||||||
|
@Test
|
||||||
|
fun create_spending_key_does_not_mutate_passed_bytes() = runTest {
|
||||||
|
val bytesOne = Mnemonics.MnemonicCode(WalletFixture.SEED_PHRASE).toEntropy()
|
||||||
|
val bytesTwo = Mnemonics.MnemonicCode(WalletFixture.SEED_PHRASE).toEntropy()
|
||||||
|
|
||||||
|
DerivationTool.deriveUnifiedSpendingKey(bytesOne, WalletFixture.NETWORK, Account.DEFAULT)
|
||||||
|
|
||||||
|
assertContentEquals(bytesTwo, bytesOne)
|
||||||
|
}
|
||||||
|
}
|
|
@ -233,7 +233,7 @@ internal class RustBackend private constructor(
|
||||||
createToAddress(
|
createToAddress(
|
||||||
dataDbFile.absolutePath,
|
dataDbFile.absolutePath,
|
||||||
usk.account.value,
|
usk.account.value,
|
||||||
usk.bytes.byteArray,
|
usk.copyBytes(),
|
||||||
to,
|
to,
|
||||||
value,
|
value,
|
||||||
memo ?: ByteArray(0),
|
memo ?: ByteArray(0),
|
||||||
|
@ -252,7 +252,7 @@ internal class RustBackend private constructor(
|
||||||
shieldToAddress(
|
shieldToAddress(
|
||||||
dataDbFile.absolutePath,
|
dataDbFile.absolutePath,
|
||||||
usk.account.value,
|
usk.account.value,
|
||||||
usk.bytes.byteArray,
|
usk.copyBytes(),
|
||||||
memo ?: ByteArray(0),
|
memo ?: ByteArray(0),
|
||||||
"$pathParamsDir/$SPEND_PARAM_FILE_NAME",
|
"$pathParamsDir/$SPEND_PARAM_FILE_NAME",
|
||||||
"$pathParamsDir/$OUTPUT_PARAM_FILE_NAME",
|
"$pathParamsDir/$OUTPUT_PARAM_FILE_NAME",
|
||||||
|
|
|
@ -11,7 +11,7 @@ import cash.z.ecc.android.sdk.jni.RustBackend
|
||||||
* derived at the time of its creation. As such, it is not suitable for long-term storage,
|
* derived at the time of its creation. As such, it is not suitable for long-term storage,
|
||||||
* export/import, or backup purposes.
|
* export/import, or backup purposes.
|
||||||
*/
|
*/
|
||||||
data class UnifiedSpendingKey internal constructor(
|
class UnifiedSpendingKey private constructor(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,12 +22,42 @@ data class UnifiedSpendingKey internal constructor(
|
||||||
* inherently unstable, and only intended to be passed between the SDK and the storage
|
* inherently unstable, and only intended to be passed between the SDK and the storage
|
||||||
* backend. Wallets **MUST NOT** allow this encoding to be exported or imported.
|
* backend. Wallets **MUST NOT** allow this encoding to be exported or imported.
|
||||||
*/
|
*/
|
||||||
internal val bytes: FirstClassByteArray
|
private val bytes: FirstClassByteArray
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
// This constructor exists solely for the JNI
|
||||||
|
internal constructor(account: Int, bytes: ByteArray) : this(Account(account), FirstClassByteArray(bytes.copyOf()))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The binary encoding of the [ZIP 316](https://zips.z.cash/zip-0316) Unified Spending
|
||||||
|
* Key for [account].
|
||||||
|
*
|
||||||
|
* This encoding **MUST NOT** be exposed to users. It is an internal encoding that is
|
||||||
|
* inherently unstable, and only intended to be passed between the SDK and the storage
|
||||||
|
* backend. Wallets **MUST NOT** allow this encoding to be exported or imported.
|
||||||
|
*/
|
||||||
|
fun copyBytes() = bytes.byteArray.copyOf()
|
||||||
|
|
||||||
// Override to prevent leaking key to logs
|
// Override to prevent leaking key to logs
|
||||||
override fun toString() = "UnifiedSpendingKey(account=$account)"
|
override fun toString() = "UnifiedSpendingKey(account=$account)"
|
||||||
|
|
||||||
fun copyBytes() = bytes.byteArray.copyOf()
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as UnifiedSpendingKey
|
||||||
|
|
||||||
|
if (account != other.account) return false
|
||||||
|
if (bytes != other.bytes) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = account.hashCode()
|
||||||
|
result = 31 * result + bytes.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class DerivationTool {
|
||||||
network: ZcashNetwork
|
network: ZcashNetwork
|
||||||
): UnifiedFullViewingKey = withRustBackendLoaded {
|
): UnifiedFullViewingKey = withRustBackendLoaded {
|
||||||
UnifiedFullViewingKey(
|
UnifiedFullViewingKey(
|
||||||
deriveUnifiedFullViewingKey(usk.bytes.byteArray, networkId = network.id)
|
deriveUnifiedFullViewingKey(usk.copyBytes(), networkId = network.id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import java.math.BigDecimal
|
||||||
import java.math.MathContext
|
import java.math.MathContext
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
internal class ConversionsTest {
|
class ConversionsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `default right padding is 6`() {
|
fun `default right padding is 6`() {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package cash.z.ecc.android.sdk.model
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
class AccountTest {
|
||||||
|
@Test
|
||||||
|
fun out_of_bounds() {
|
||||||
|
assertFailsWith(IllegalArgumentException::class) {
|
||||||
|
Account(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue