mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2025-04-14 04:53:06 -04:00
lib: fix instrumented tests
This commit is contained in:
parent
181b3dc442
commit
a664ce1652
@ -1,7 +1,7 @@
|
||||
package im.molly.monero
|
||||
|
||||
import android.os.Parcel
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -12,13 +12,13 @@ class SecretKeyParcelableTest {
|
||||
val secret = Random.nextBytes(32)
|
||||
val originalKey = SecretKey(secret)
|
||||
|
||||
val parcel = Parcel.obtain()
|
||||
val parcel = Parcel.obtain().apply {
|
||||
originalKey.writeToParcel(this, 0)
|
||||
setDataPosition(0)
|
||||
}
|
||||
|
||||
originalKey.writeToParcel(parcel, 0)
|
||||
val recreatedKey = SecretKey.CREATOR.createFromParcel(parcel)
|
||||
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val key = SecretKey.create(parcel)
|
||||
Truth.assertThat(key == originalKey).isTrue()
|
||||
assertThat(recreatedKey).isEqualTo(originalKey)
|
||||
}
|
||||
}
|
||||
|
@ -2,63 +2,64 @@ package im.molly.monero
|
||||
|
||||
import androidx.test.filters.LargeTest
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class WalletNativeTest {
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
fun keyGenerationIsDeterministic() {
|
||||
fun keyGenerationIsDeterministic() = runTest {
|
||||
assertThat(
|
||||
WalletNative.localSyncWallet(
|
||||
networkId = MoneroNetwork.Mainnet.id,
|
||||
secretSpendKey = SecretKey("d2ca26e22489bd9871c910c58dee3ab08e66b9d566825a064c8c0af061cd8706".parseHex()),
|
||||
).primaryAccountAddress
|
||||
).publicAddress
|
||||
).isEqualTo("4AYjQM9HoAFNUeC3cvSfgeAN89oMMpMqiByvunzSzhn97cj726rJj3x8hCbH58UnMqQJShczCxbpWRiCJQ3HCUDHLiKuo4T")
|
||||
|
||||
assertThat(
|
||||
WalletNative.localSyncWallet(
|
||||
networkId = MoneroNetwork.Testnet.id,
|
||||
secretSpendKey = SecretKey("48a35268bc33227eea43ac1ecfd144d51efc023c115c26ca68a01cc6201e9900".parseHex()),
|
||||
).primaryAccountAddress
|
||||
).publicAddress
|
||||
).isEqualTo("A1v6gVUcGgGE87c1uFRWB1KfPVik2qLLDJiZT3rhZ8qjF3BGA6oHzeDboD23dH8rFaFFcysyqwF6DBj8WUTBWwEhESB7nZz")
|
||||
|
||||
assertThat(
|
||||
WalletNative.localSyncWallet(
|
||||
networkId = MoneroNetwork.Stagenet.id,
|
||||
secretSpendKey = SecretKey("561a8d4e121ffca7321a7dc6af79679ceb4cdc8c0dcb0ef588b574586c5fac04".parseHex()),
|
||||
).primaryAccountAddress
|
||||
).publicAddress
|
||||
).isEqualTo("54kPaUhYgGNBT72N8Bv2DFMqstLGJCEcWg1EAjwpxABkKL3uBtBLAh4VAPKvhWBdaD4ZpiftA8YWFLAxnWL4aQ9TD4vhY4W")
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
fun publicAddressesAreDistinct() {
|
||||
fun publicAddressesAreDistinct() = runTest {
|
||||
val publicAddress =
|
||||
WalletNative.localSyncWallet(
|
||||
networkId = MoneroNetwork.Mainnet.id,
|
||||
secretSpendKey = randomSecretKey(),
|
||||
).primaryAccountAddress
|
||||
).publicAddress
|
||||
|
||||
val anotherPublicAddress =
|
||||
WalletNative.localSyncWallet(
|
||||
networkId = MoneroNetwork.Mainnet.id,
|
||||
secretSpendKey = randomSecretKey(),
|
||||
).primaryAccountAddress
|
||||
).publicAddress
|
||||
|
||||
assertThat(publicAddress).isNotEqualTo(anotherPublicAddress)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun atGenesisBalanceIsZero() {
|
||||
fun atGenesisBalanceIsZero() = runTest {
|
||||
with(
|
||||
WalletNative.localSyncWallet(
|
||||
networkId = MoneroNetwork.Mainnet.id,
|
||||
secretSpendKey = randomSecretKey(),
|
||||
).currentBalance
|
||||
).getLedger()
|
||||
) {
|
||||
assertThat(totalAmount).isEqualTo(0.toAtomicUnits())
|
||||
assertThat(totalAmountUnlockedAt(1)).isEqualTo(0.toAtomicUnits())
|
||||
assertThat(transactions).isEmpty()
|
||||
assertThat(isBalanceZero).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ void Wallet::restoreAccount(const std::vector<char>& secret_scalar, uint64_t res
|
||||
std::lock_guard<std::mutex> lock(m_wallet_mutex);
|
||||
auto& account = m_wallet.get_account();
|
||||
GenerateAccountKeys(account, secret_scalar);
|
||||
m_subaddresses[{0, 0}] = m_wallet.get_subaddress_as_str({0, 0});
|
||||
if (restore_point < CRYPTONOTE_MAX_BLOCK_NUMBER) {
|
||||
m_restore_height = restore_point;
|
||||
m_last_block_timestamp = 0;
|
||||
|
@ -10,6 +10,9 @@ data class Ledger(
|
||||
val transactions: Collection<Transaction>
|
||||
get() = transactionById.values
|
||||
|
||||
val isBalanceZero: Boolean
|
||||
get() = getBalance().totalAmount.isZero
|
||||
|
||||
fun getBalance(): Balance = enoteSet.calculateBalance()
|
||||
|
||||
fun getBalanceForAccount(accountIndex: Int): Balance =
|
||||
|
@ -1,7 +1,7 @@
|
||||
package im.molly.monero
|
||||
|
||||
import im.molly.monero.internal.LedgerFactory
|
||||
import im.molly.monero.internal.TxInfo
|
||||
import im.molly.monero.internal.consolidateTransactions
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
@ -86,7 +86,7 @@ class MoneroWallet internal constructor(
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
wallet.getAddressesForAccount(accountIndex, object : BaseWalletCallbacks() {
|
||||
override fun onSubAddressListReceived(subAddresses: Array<String>) {
|
||||
val accounts = parseAndAggregateAddresses(subAddresses)
|
||||
val accounts = parseAndAggregateAddresses(subAddresses.asIterable())
|
||||
continuation.resume(accounts.single()) {}
|
||||
}
|
||||
})
|
||||
@ -96,23 +96,12 @@ class MoneroWallet internal constructor(
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
wallet.getAllAddresses(object : BaseWalletCallbacks() {
|
||||
override fun onSubAddressListReceived(subAddresses: Array<String>) {
|
||||
val accounts = parseAndAggregateAddresses(subAddresses)
|
||||
val accounts = parseAndAggregateAddresses(subAddresses.asIterable())
|
||||
continuation.resume(accounts) {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun parseAndAggregateAddresses(subAddresses: Array<String>): List<WalletAccount> =
|
||||
subAddresses.map { AccountAddress.parseWithIndexes(it) }
|
||||
.groupBy { it.accountIndex }
|
||||
.map { (index, addresses) ->
|
||||
WalletAccount(
|
||||
addresses = addresses,
|
||||
accountIndex = index,
|
||||
)
|
||||
}
|
||||
.sortedBy { it.accountIndex }
|
||||
|
||||
/**
|
||||
* A [Flow] of ledger changes.
|
||||
*/
|
||||
@ -125,17 +114,11 @@ class MoneroWallet internal constructor(
|
||||
subAddresses: Array<String>,
|
||||
blockchainTime: BlockchainTime,
|
||||
) {
|
||||
val indexedAccounts = parseAndAggregateAddresses(subAddresses)
|
||||
val (txById, enotes) = txHistory.consolidateTransactions(
|
||||
accounts = indexedAccounts,
|
||||
blockchainContext = blockchainTime,
|
||||
)
|
||||
val ledger = Ledger(
|
||||
publicAddress = publicAddress,
|
||||
indexedAccounts = indexedAccounts,
|
||||
transactionById = txById,
|
||||
enoteSet = enotes,
|
||||
checkedAt = blockchainTime,
|
||||
val accounts = parseAndAggregateAddresses(subAddresses.asIterable())
|
||||
val ledger = LedgerFactory.createFromTxHistory(
|
||||
txHistory = txHistory,
|
||||
accounts = accounts,
|
||||
blockchainTime = blockchainTime,
|
||||
)
|
||||
sendLedger(ledger)
|
||||
}
|
||||
@ -145,7 +128,7 @@ class MoneroWallet internal constructor(
|
||||
}
|
||||
|
||||
override fun onSubAddressListUpdated(subAddresses: Array<String>) {
|
||||
val accountsUpdated = parseAndAggregateAddresses(subAddresses)
|
||||
val accountsUpdated = parseAndAggregateAddresses(subAddresses.asIterable())
|
||||
if (lastKnownLedger.indexedAccounts != accountsUpdated) {
|
||||
sendLedger(lastKnownLedger.copy(indexedAccounts = accountsUpdated))
|
||||
}
|
||||
|
@ -13,3 +13,11 @@ fun Iterable<WalletAccount>.findAddressByIndex(
|
||||
it.accountIndex == accountIndex && it.subAddressIndex == subAddressIndex
|
||||
}
|
||||
}
|
||||
|
||||
fun parseAndAggregateAddresses(addresses: Iterable<String>): List<WalletAccount> {
|
||||
return addresses
|
||||
.map { AccountAddress.parseWithIndexes(it) }
|
||||
.groupBy { it.accountIndex }
|
||||
.toSortedMap()
|
||||
.map { (index, addresses) -> WalletAccount(addresses, index) }
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package im.molly.monero
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
@ -7,3 +9,16 @@ interface WalletDataStore {
|
||||
suspend fun write(writer: (OutputStream) -> Unit)
|
||||
suspend fun read(): InputStream
|
||||
}
|
||||
|
||||
class InMemoryWalletDataStore : WalletDataStore {
|
||||
private val data = ByteArrayOutputStream()
|
||||
|
||||
override suspend fun write(writer: (OutputStream) -> Unit) {
|
||||
data.reset()
|
||||
writer(data)
|
||||
}
|
||||
|
||||
override suspend fun read(): InputStream {
|
||||
return ByteArrayInputStream(data.toByteArray())
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import im.molly.monero.internal.HttpRequest
|
||||
import im.molly.monero.internal.HttpResponse
|
||||
import im.molly.monero.internal.IHttpRequestCallback
|
||||
import im.molly.monero.internal.IHttpRpcClient
|
||||
import im.molly.monero.internal.LedgerFactory
|
||||
import im.molly.monero.internal.TxInfo
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.Closeable
|
||||
@ -27,7 +28,7 @@ internal class WalletNative private constructor(
|
||||
companion object {
|
||||
suspend fun localSyncWallet(
|
||||
networkId: Int,
|
||||
storageAdapter: IStorageAdapter,
|
||||
storageAdapter: IStorageAdapter = StorageAdapter(InMemoryWalletDataStore()),
|
||||
rpcClient: IHttpRpcClient? = null,
|
||||
secretSpendKey: SecretKey? = null,
|
||||
restorePoint: Long? = null,
|
||||
@ -105,7 +106,26 @@ internal class WalletNative private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPublicAddress() = nativeGetPublicAddress(handle)
|
||||
override fun getPublicAddress(): String = nativeGetPublicAddress(handle)
|
||||
|
||||
fun getCurrentBlockchainTime(): BlockchainTime {
|
||||
return network.blockchainTime(
|
||||
nativeGetCurrentBlockchainHeight(handle),
|
||||
nativeGetCurrentBlockchainTimestamp(handle),
|
||||
)
|
||||
}
|
||||
|
||||
fun getAllAccounts(): List<WalletAccount> {
|
||||
return parseAndAggregateAddresses(getSubAddresses().asIterable())
|
||||
}
|
||||
|
||||
fun getLedger(): Ledger {
|
||||
return LedgerFactory.createFromTxHistory(
|
||||
txHistory = getTxHistorySnapshot(),
|
||||
accounts = getAllAccounts(),
|
||||
blockchainTime = getCurrentBlockchainTime(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MoneroNetwork.blockchainTime(height: Int, epochSecond: Long): BlockchainTime {
|
||||
// Block timestamp could be zero during a fast refresh.
|
||||
@ -116,15 +136,6 @@ internal class WalletNative private constructor(
|
||||
return BlockchainTime(height = height, timestamp = timestamp, network = this)
|
||||
}
|
||||
|
||||
val currentBlockchainTime: BlockchainTime
|
||||
get() = network.blockchainTime(
|
||||
nativeGetCurrentBlockchainHeight(handle),
|
||||
nativeGetCurrentBlockchainTimestamp(handle),
|
||||
)
|
||||
|
||||
val currentBalance: Balance
|
||||
get() = TODO() // txHistorySnapshot().consolidateTransactions().second.balance()
|
||||
|
||||
private fun getSubAddresses(accountIndex: Int? = null): Array<String> {
|
||||
return nativeGetSubAddresses(accountIndex ?: -1, handle)
|
||||
}
|
||||
@ -155,7 +166,7 @@ internal class WalletNative private constructor(
|
||||
nativeCancelRefresh(handle)
|
||||
}
|
||||
}
|
||||
callback?.onRefreshResult(currentBlockchainTime, status)
|
||||
callback?.onRefreshResult(getCurrentBlockchainTime(), status)
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,7 +279,7 @@ internal class WalletNative private constructor(
|
||||
|
||||
balanceListenersLock.withLock {
|
||||
balanceListeners.add(listener)
|
||||
listener.onBalanceChanged(txHistory, subAddresses, currentBlockchainTime)
|
||||
listener.onBalanceChanged(txHistory, subAddresses, getCurrentBlockchainTime())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
package im.molly.monero.internal
|
||||
|
||||
import im.molly.monero.BlockchainTime
|
||||
import im.molly.monero.Ledger
|
||||
import im.molly.monero.WalletAccount
|
||||
import im.molly.monero.findAddressByIndex
|
||||
|
||||
internal object LedgerFactory {
|
||||
fun createFromTxHistory(
|
||||
txHistory: List<TxInfo>,
|
||||
accounts: List<WalletAccount>,
|
||||
blockchainTime: BlockchainTime,
|
||||
): Ledger {
|
||||
val (txById, enotes) = txHistory.consolidateTransactions(
|
||||
accounts = accounts,
|
||||
blockchainContext = blockchainTime,
|
||||
)
|
||||
val publicAddress = accounts.findAddressByIndex(accountIndex = 0)
|
||||
checkNotNull(publicAddress)
|
||||
return Ledger(
|
||||
publicAddress = publicAddress,
|
||||
indexedAccounts = accounts,
|
||||
transactionById = txById,
|
||||
enoteSet = enotes,
|
||||
checkedAt = blockchainTime,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user