parent
4c2fdeeb73
commit
e95e43b83b
|
@ -81,3 +81,4 @@ DecompileChecker.kt
|
|||
backup-dbs/
|
||||
*.db
|
||||
.DS_Store
|
||||
/.idea/androidTestResultsUserPreferences.xml
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
DerivationTool.deriveTransparentAccountPrivateKey(
|
||||
SEED,
|
||||
network =
|
||||
network,
|
||||
network = network,
|
||||
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(
|
||||
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",
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class DerivationTool {
|
|||
network: ZcashNetwork
|
||||
): UnifiedFullViewingKey = withRustBackendLoaded {
|
||||
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 kotlin.test.assertEquals
|
||||
|
||||
internal class ConversionsTest {
|
||||
class ConversionsTest {
|
||||
|
||||
@Test
|
||||
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