Add simple tests for Account and UnifiedSpendingKey

test
This commit is contained in:
Carter Jernigan 2022-10-04 08:25:13 -04:00 committed by Carter Jernigan
parent 4c2fdeeb73
commit e95e43b83b
11 changed files with 141 additions and 11 deletions

1
.gitignore vendored
View File

@ -81,3 +81,4 @@ DecompileChecker.kt
backup-dbs/
*.db
.DS_Store
/.idea/androidTestResultsUserPreferences.xml

View File

@ -287,8 +287,6 @@ dependencies {
// Tests
testImplementation(libs.kotlin.reflect)
testImplementation(libs.kotlin.test)
testImplementation(libs.mockito.junit)
testImplementation(libs.mockito.kotlin)
testImplementation(libs.bundles.junit)
testImplementation(libs.grpc.testing)

View File

@ -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)
}

View File

@ -28,8 +28,7 @@ class TransparentTest(val expected: Expected, val network: ZcashNetwork) {
expected.tAccountPrivKey,
DerivationTool.deriveTransparentAccountPrivateKey(
SEED,
network =
network,
network = network,
Account.DEFAULT
)
)

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -233,7 +233,7 @@ internal class RustBackend private constructor(
createToAddress(
dataDbFile.absolutePath,
usk.account.value,
usk.bytes.byteArray,
usk.copyBytes(),
to,
value,
memo ?: ByteArray(0),
@ -252,7 +252,7 @@ internal class RustBackend private constructor(
shieldToAddress(
dataDbFile.absolutePath,
usk.account.value,
usk.bytes.byteArray,
usk.copyBytes(),
memo ?: ByteArray(0),
"$pathParamsDir/$SPEND_PARAM_FILE_NAME",
"$pathParamsDir/$OUTPUT_PARAM_FILE_NAME",

View File

@ -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,
* export/import, or backup purposes.
*/
data class UnifiedSpendingKey internal constructor(
class UnifiedSpendingKey private constructor(
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
* 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 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 {

View File

@ -45,7 +45,7 @@ class DerivationTool {
network: ZcashNetwork
): UnifiedFullViewingKey = withRustBackendLoaded {
UnifiedFullViewingKey(
deriveUnifiedFullViewingKey(usk.bytes.byteArray, networkId = network.id)
deriveUnifiedFullViewingKey(usk.copyBytes(), networkId = network.id)
)
}

View File

@ -6,7 +6,7 @@ import java.math.BigDecimal
import java.math.MathContext
import kotlin.test.assertEquals
internal class ConversionsTest {
class ConversionsTest {
@Test
fun `default right padding is 6`() {

View File

@ -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)
}
}
}