Skip to content

Commit

Permalink
Merge pull request #1606 from novasamatech/rc/8.3.0
Browse files Browse the repository at this point in the history
Rc/8.3.0
  • Loading branch information
antonijzelinskij authored Jul 23, 2024
2 parents 78c15a5 + 628e34b commit 9a9cb8e
Show file tree
Hide file tree
Showing 18 changed files with 126 additions and 93 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
buildscript {
ext {
// App version
versionName = '8.2.2'
versionCode = 146
versionName = '8.3.0'
versionCode = 148

applicationId = "io.novafoundation.nova"
releaseApplicationSuffix = "market"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,9 @@ inline fun <T, R> Iterable<T>.foldToSet(mapper: (T) -> Iterable<R>): Set<R> = fo

inline fun <T, R : Any> Iterable<T>.mapNotNullToSet(mapper: (T) -> R?): Set<R> = mapNotNullTo(mutableSetOf(), mapper)

fun <T> List<T>.indexOfFirstOrNull(predicate: (T) -> Boolean) = indexOfFirst(predicate).takeIf { it >= 0 }
fun <T> Collection<T>.indexOfFirstOrNull(predicate: (T) -> Boolean) = indexOfFirst(predicate).takeIf { it >= 0 }

fun <T> List<T>.indexOfOrNull(value: T) = indexOf(value).takeIf { it >= 0 }
fun <T> Collection<T>.indexOfOrNull(value: T) = indexOf(value).takeIf { it >= 0 }

@Suppress("IfThenToElvis")
fun ByteArray?.optionalContentEquals(other: ByteArray?): Boolean {
Expand Down
2 changes: 2 additions & 0 deletions feature-staking-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ android {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

buildConfigField "String", "GLOBAL_CONFIG_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/global_config_dev.json\""
buildConfigField "String", "RECOMMENDED_VALIDATORS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/validators/v1/nova_validators_dev.json\""
}

buildTypes {
Expand All @@ -23,6 +24,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

buildConfigField "String", "GLOBAL_CONFIG_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/global_config.json\""
buildConfigField "String", "RECOMMENDED_VALIDATORS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/validators/v1/nova_validators.json\""
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package io.novafoundation.nova.feature_staking_impl.data.validators

import io.novafoundation.nova.feature_staking_impl.BuildConfig
import retrofit2.http.GET

interface NovaValidatorsApi {

@GET("https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/nova_validators.json")
suspend fun getValidators(): Map<String, List<String>>
@GET(BuildConfig.RECOMMENDED_VALIDATORS_URL)
suspend fun getValidators(): ValidatorsPreferencesRemote
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.novafoundation.nova.feature_staking_impl.data.validators

class ValidatorsPreferencesRemote(
val preferred: Map<String, Set<String>>,
val excluded: Map<String, Set<String>>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.novafoundation.nova.feature_staking_impl.data.validators

import io.novafoundation.nova.common.utils.mapNotNullToSet
import io.novafoundation.nova.common.utils.mapValuesNotNull
import io.novafoundation.nova.runtime.ext.accountIdOf
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
import io.novafoundation.nova.runtime.multiNetwork.chainsById
import io.novasama.substrate_sdk_android.extensions.toHexString
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

interface ValidatorsPreferencesSource {

suspend fun getRecommendedValidatorIds(chainId: ChainId): Set<String>

suspend fun getExcludedValidatorIds(chainId: ChainId): Set<String>
}

class RemoteValidatorsPreferencesSource(
private val validatorsApi: NovaValidatorsApi,
private val chainRegistry: ChainRegistry,
) : ValidatorsPreferencesSource {

private var validatorsPreferences: ValidatorsPreferencesRemote? = null
private val validatorsMutex = Mutex()

override suspend fun getRecommendedValidatorIds(chainId: ChainId): Set<String> {
return getValidators().preferred[chainId].orEmpty()
}

override suspend fun getExcludedValidatorIds(chainId: ChainId): Set<String> {
return getValidators().excluded[chainId].orEmpty()
}

private suspend fun getValidators(): ValidatorsPreferencesRemote {
return validatorsMutex.withLock {
if (validatorsPreferences == null) {
validatorsPreferences = fetchValidators()
}

validatorsPreferences ?: ValidatorsPreferencesRemote(emptyMap(), emptyMap())
}
}

private suspend fun fetchValidators(): ValidatorsPreferencesRemote? {
return runCatching {
val chainsById = chainRegistry.chainsById()
val preferences = validatorsApi.getValidators()
val recommended = preferences.preferred.mapValuesNotNull { (chainId, addresses) ->
chainsById[chainId]?.convertAddressesToAccountIds(addresses)
}
val excluded = preferences.excluded.mapValuesNotNull { (chainId, addresses) ->
chainsById[chainId]?.convertAddressesToAccountIds(addresses)
}

ValidatorsPreferencesRemote(recommended, excluded)
}.getOrNull()
}

private fun Chain.convertAddressesToAccountIds(addresses: Set<String>): Set<String> {
return addresses.mapNotNullToSet {
this.tryConvertAddressToAccountIdHex(it)
}
}

private fun Chain.tryConvertAddressToAccountIdHex(address: String): String? {
return runCatching { accountIdOf(address).toHexString() }.getOrNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.re
import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.reward.PoolStakingRewardsDataSource
import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.reward.RealStakingRewardsDataSourceRegistry
import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.reward.StakingRewardsDataSourceRegistry
import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators
import io.novafoundation.nova.feature_staking_impl.data.validators.ValidatorsPreferencesSource
import io.novafoundation.nova.feature_staking_impl.data.validators.NovaValidatorsApi
import io.novafoundation.nova.feature_staking_impl.data.validators.RemoteKnownNovaValidators
import io.novafoundation.nova.feature_staking_impl.data.validators.RemoteValidatorsPreferencesSource
import io.novafoundation.nova.feature_staking_impl.di.staking.DefaultBulkRetriever
import io.novafoundation.nova.feature_staking_impl.di.staking.PayoutsBulkRetriever
import io.novafoundation.nova.feature_staking_impl.domain.StakingInteractor
Expand Down Expand Up @@ -334,16 +334,16 @@ class StakingFeatureModule {
fun provideKnownNovaValidators(
novaValidatorsApi: NovaValidatorsApi,
chainRegistry: ChainRegistry
): KnownNovaValidators = RemoteKnownNovaValidators(novaValidatorsApi, chainRegistry)
): ValidatorsPreferencesSource = RemoteValidatorsPreferencesSource(novaValidatorsApi, chainRegistry)

@Provides
@FeatureScope
fun provideValidatorRecommendatorFactory(
validatorProvider: ValidatorProvider,
computationalCache: ComputationalCache,
sharedState: StakingSharedState,
knownNovaValidators: KnownNovaValidators
) = ValidatorRecommenderFactory(validatorProvider, sharedState, computationalCache, knownNovaValidators)
validatorsPreferencesSource: ValidatorsPreferencesSource
) = ValidatorRecommenderFactory(validatorProvider, sharedState, computationalCache, validatorsPreferencesSource)

@Provides
@FeatureScope
Expand All @@ -353,14 +353,14 @@ class StakingFeatureModule {
rewardCalculatorFactory: RewardCalculatorFactory,
stakingConstantsRepository: StakingConstantsRepository,
stakingSharedComputation: StakingSharedComputation,
knownNovaValidators: KnownNovaValidators
validatorsPreferencesSource: ValidatorsPreferencesSource
) = ValidatorProvider(
stakingRepository = stakingRepository,
identityRepository = identityRepository,
rewardCalculatorFactory = rewardCalculatorFactory,
stakingConstantsRepository = stakingConstantsRepository,
stakingSharedComputation = stakingSharedComputation,
knownNovaValidators = knownNovaValidators
validatorsPreferencesSource = validatorsPreferencesSource
)

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.reposit
import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.turing.repository.TuringStakingRewardsRepository
import io.novafoundation.nova.feature_staking_impl.data.repository.StakingPeriodRepository
import io.novafoundation.nova.feature_staking_impl.data.repository.StakingRewardsRepository
import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators
import io.novafoundation.nova.feature_staking_impl.data.validators.ValidatorsPreferencesSource
import io.novafoundation.nova.feature_staking_impl.di.staking.parachain.start.StartParachainStakingFlowModule
import io.novafoundation.nova.feature_staking_impl.di.staking.parachain.turing.TuringStakingModule
import io.novafoundation.nova.feature_staking_impl.di.staking.parachain.unbond.ParachainStakingUnbondModule
Expand Down Expand Up @@ -149,7 +149,7 @@ class ParachainStakingModule {
fun provideCollatorRecommendatorFactory(
collatorProvider: CollatorProvider,
computationalCache: ComputationalCache,
knownNovaCollators: KnownNovaValidators
knownNovaCollators: ValidatorsPreferencesSource
) = CollatorRecommendatorFactory(collatorProvider, computationalCache, knownNovaCollators)

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import io.novafoundation.nova.common.data.memory.ComputationalCache
import io.novafoundation.nova.common.utils.indexOfOrNull
import io.novafoundation.nova.feature_staking_impl.data.StakingOption
import io.novafoundation.nova.feature_staking_impl.data.chain
import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators
import io.novafoundation.nova.feature_staking_impl.data.validators.ValidatorsPreferencesSource
import io.novafoundation.nova.feature_staking_impl.domain.parachainStaking.common.CollatorProvider
import io.novafoundation.nova.feature_staking_impl.domain.parachainStaking.common.CollatorProvider.CollatorSource
import io.novafoundation.nova.feature_staking_impl.domain.parachainStaking.common.model.Collator
import kotlinx.coroutines.CoroutineScope

class CollatorRecommendator(private val allCollators: List<Collator>, private val novaCollatorIds: List<String>) {
class CollatorRecommendator(private val allCollators: List<Collator>, private val novaCollatorIds: Set<String>) {

fun recommendations(config: CollatorRecommendationConfig): List<Collator> {
return allCollators.sortedWith(config.sorting)
Expand All @@ -29,13 +29,13 @@ private const val COLLATORS_CACHE = "COLLATORS_CACHE"
class CollatorRecommendatorFactory(
private val collatorProvider: CollatorProvider,
private val computationalCache: ComputationalCache,
private val knownNovaValidators: KnownNovaValidators
private val validatorsPreferencesSource: ValidatorsPreferencesSource
) {

suspend fun create(stakingOption: StakingOption, scope: CoroutineScope) = computationalCache.useCache(COLLATORS_CACHE, scope) {
val collators = collatorProvider.getCollators(stakingOption, CollatorSource.Elected)

val knownNovaCollators = knownNovaValidators.getValidatorIds(stakingOption.chain.id)
val knownNovaCollators = validatorsPreferencesSource.getRecommendedValidatorIds(stakingOption.chain.id)

CollatorRecommendator(collators, knownNovaCollators)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import kotlinx.coroutines.withContext

class ValidatorRecommender(
val availableValidators: List<Validator>,
private val novaValidatorIds: List<String>,
private val novaValidatorIds: Set<String>,
private val excludedValidators: Set<String>,
) {

suspend fun recommendations(settings: RecommendationSettings) = withContext(Dispatchers.Default) {
val all = availableValidators.applyFiltersAdaptingToEmptyResult(settings.allFilters)
.filterExcludedIfNeeded(settings)
.sortedWith(settings.sorting)

val postprocessed = settings.postProcessors.fold(all) { acc, postProcessor ->
Expand Down Expand Up @@ -50,4 +52,10 @@ class ValidatorRecommender(

return filtered
}

private fun List<Validator>.filterExcludedIfNeeded(settings: RecommendationSettings): List<Validator> {
if (!settings.filterExcluded) return this

return filter { it.accountIdHex !in excludedValidators }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.novafoundation.nova.feature_staking_impl.domain.recommendations
import io.novafoundation.nova.common.data.memory.ComputationalCache
import io.novafoundation.nova.feature_staking_impl.data.StakingSharedState
import io.novafoundation.nova.feature_staking_impl.data.chain
import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators
import io.novafoundation.nova.feature_staking_impl.data.validators.ValidatorsPreferencesSource
import io.novafoundation.nova.feature_staking_impl.domain.validators.ValidatorProvider
import io.novafoundation.nova.feature_staking_impl.domain.validators.ValidatorSource
import io.novafoundation.nova.runtime.state.selectedOption
Expand All @@ -17,7 +17,7 @@ class ValidatorRecommenderFactory(
private val validatorProvider: ValidatorProvider,
private val sharedState: StakingSharedState,
private val computationalCache: ComputationalCache,
private val knownNovaValidators: KnownNovaValidators,
private val validatorsPreferencesSource: ValidatorsPreferencesSource,
) {

suspend fun awaitRecommendatorLoading(scope: CoroutineScope) = withContext(Dispatchers.IO) {
Expand All @@ -32,9 +32,12 @@ class ValidatorRecommenderFactory(
val stakingOption = sharedState.selectedOption()

val sources = listOf(ValidatorSource.Elected, ValidatorSource.NovaValidators)

val validators = validatorProvider.getValidators(stakingOption, sources, scope)
val knownNovaValidatorIds = knownNovaValidators.getValidatorIds(stakingOption.chain.id)

ValidatorRecommender(validators, knownNovaValidatorIds)
val recommendedValidators = validatorsPreferencesSource.getRecommendedValidatorIds(stakingOption.chain.id)
val excludedValidators = validatorsPreferencesSource.getExcludedValidatorIds(stakingOption.chain.id)

ValidatorRecommender(validators, recommendedValidators, excludedValidators)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data class RecommendationSettings(
val customEnabledFilters: List<RecommendationFilter>,
val postProcessors: List<RecommendationPostProcessor>,
val sorting: RecommendationSorting,
val filterExcluded: Boolean,
val limit: Int? = null
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ class RecommendationSettingsProvider(

fun currentSettings() = customSettingsFlow.value

fun defaultSettings(maximumValidatorsPerNominator: Int): RecommendationSettings {
fun recommendedSettings(maximumValidatorsPerNominator: Int): RecommendationSettings {
return RecommendationSettings(
alwaysEnabledFilters = alwaysEnabledFilters,
customEnabledFilters = customizableFilters,
sorting = APYSorting,
postProcessors = allPostProcessors,
filterExcluded = true,
limit = maximumValidatorsPerNominator
)
}
Expand All @@ -71,6 +72,7 @@ class RecommendationSettingsProvider(
customEnabledFilters = customizableFilters,
sorting = APYSorting,
postProcessors = allPostProcessors,
filterExcluded = false,
limit = null
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class DirectStakingRecommendation(
override suspend fun recommendedSelection(stake: Balance): StartMultiStakingSelection {
val provider = recommendationSettingsProvider.await()
val maximumValidatorsPerNominator = stakingConstantsRepository.maxValidatorsPerNominator(stakingOption.chain.id, stake)
val recommendationSettings = provider.defaultSettings(maximumValidatorsPerNominator)
val recommendationSettings = provider.recommendedSettings(maximumValidatorsPerNominator)
val recommendator = recommendator.await()

val recommendedValidators = recommendator.recommendations(recommendationSettings)
Expand Down
Loading

0 comments on commit 9a9cb8e

Please sign in to comment.