diff --git a/CHANGELOG.md b/CHANGELOG.md index 32185ef4..3d551669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * Now caching FT/NFT counts per address #448 * This is done on the fly, asynchronously * The Account detail API pulls from the cached values +* Add `isUnderMaintenance` flag #459 + * Allows the service to function properly without having to hit the node; ideal for when the node is upgrading ### Data * Migration 1.86 - Add `process_queue`, `account_token_counts` #448 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index bc2f1fef..03df5dd1 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -41,6 +41,7 @@ services: - EXPLORER_UTILITY_TOKEN_DEFAULT_GAS_PRICE=1905 - EXPLORER_UTILITY_TOKEN_BASE_DECIMAL_PLACES=9 - EXPLORER_VOTING_POWER_PADDING=1000000 + - EXPLORER_IS_UNDER_MAINTENANCE=false depends_on: - explorer-postgres links: diff --git a/service/src/main/kotlin/io/provenance/explorer/Application.kt b/service/src/main/kotlin/io/provenance/explorer/Application.kt index b607d2d5..a7cde64c 100644 --- a/service/src/main/kotlin/io/provenance/explorer/Application.kt +++ b/service/src/main/kotlin/io/provenance/explorer/Application.kt @@ -14,7 +14,6 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.ComponentScan -import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.web.servlet.config.annotation.EnableWebMvc import springfox.documentation.swagger2.annotations.EnableSwagger2 import java.util.TimeZone @@ -22,7 +21,6 @@ import java.util.TimeZone @ComponentScan(basePackages = ["io.provenance.explorer" ]) @EnableAutoConfiguration(exclude = [HibernateJpaAutoConfiguration::class]) @EnableConfigurationProperties(value = [ExplorerProperties::class]) -@EnableScheduling @EnableWebMvc @EnableSwagger2 class Application diff --git a/service/src/main/kotlin/io/provenance/explorer/config/AppConfig.kt b/service/src/main/kotlin/io/provenance/explorer/config/AppConfig.kt index 7d6c4ff5..f4a8be99 100644 --- a/service/src/main/kotlin/io/provenance/explorer/config/AppConfig.kt +++ b/service/src/main/kotlin/io/provenance/explorer/config/AppConfig.kt @@ -1,7 +1,9 @@ package io.provenance.explorer.config import io.provenance.explorer.config.interceptor.JwtInterceptor +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @@ -19,3 +21,8 @@ class AppConfig : WebMvcConfigurer { ) } } + +@Configuration +@EnableScheduling +@ConditionalOnProperty(name = ["is-under-maintenance"], prefix = "explorer", havingValue = "false") +class SchedulerConfig diff --git a/service/src/main/kotlin/io/provenance/explorer/config/ExplorerProperties.kt b/service/src/main/kotlin/io/provenance/explorer/config/ExplorerProperties.kt index d1293661..2d4b0a0b 100644 --- a/service/src/main/kotlin/io/provenance/explorer/config/ExplorerProperties.kt +++ b/service/src/main/kotlin/io/provenance/explorer/config/ExplorerProperties.kt @@ -22,7 +22,8 @@ class ExplorerProperties( val swaggerUrl: String, val swaggerProtocol: String, val pricingUrl: String, - val feeBugRangeOneEleven: List // [0] is the beginning of the range, [1] is the end of the range, inclusive + val feeBugRangeOneEleven: List, // [0] is the beginning of the range, [1] is the end of the range, inclusive + val isUnderMaintenance: Boolean ) { fun initialHistoricalDays() = initialHistoricalDayCount.toInt() @@ -47,6 +48,7 @@ class ExplorerProperties( var PROV_ACC_PREFIX = Bech32.PROVENANCE_TESTNET_ACCOUNT_PREFIX var PROV_VAL_OPER_PREFIX = Bech32.PROVENANCE_TESTNET_VALIDATOR_ACCOUNT_PREFIX var PROV_VAL_CONS_PREFIX = Bech32.PROVENANCE_TESTNET_CONSENSUS_ACCOUNT_PREFIX + var UNDER_MAINTENANCE = false } @Value("\${explorer.utility-token}") @@ -82,4 +84,9 @@ class ExplorerProperties( PROV_VAL_CONS_PREFIX = Bech32.PROVENANCE_TESTNET_CONSENSUS_ACCOUNT_PREFIX } } + + @Value("\${explorer.is-under-maintenance}") + fun setUnderMaintenance(isUnderMaintenance: Boolean) { + UNDER_MAINTENANCE = isUnderMaintenance + } } diff --git a/service/src/main/kotlin/io/provenance/explorer/domain/entities/Validators.kt b/service/src/main/kotlin/io/provenance/explorer/domain/entities/Validators.kt index 5fe7ea57..30299300 100644 --- a/service/src/main/kotlin/io/provenance/explorer/domain/entities/Validators.kt +++ b/service/src/main/kotlin/io/provenance/explorer/domain/entities/Validators.kt @@ -58,6 +58,12 @@ class ValidatorsCacheRecord(id: EntityID) : CacheEntity(id) { .map { it[BlockCacheTable.height] } .toSet() } + + fun getLatestByHeight() = transaction { + ValidatorsCacheRecord.all().orderBy(Pair(ValidatorsCacheTable.id, SortOrder.DESC)).first().validators + } + + fun getAtHeight(height: Long) = transaction { ValidatorsCacheRecord.findById(height.toInt())!!.validators } } var height by ValidatorsCacheTable.height diff --git a/service/src/main/kotlin/io/provenance/explorer/domain/extensions/CoinExtensions.kt b/service/src/main/kotlin/io/provenance/explorer/domain/extensions/CoinExtensions.kt index a1edfcc7..d9fefa02 100644 --- a/service/src/main/kotlin/io/provenance/explorer/domain/extensions/CoinExtensions.kt +++ b/service/src/main/kotlin/io/provenance/explorer/domain/extensions/CoinExtensions.kt @@ -90,3 +90,8 @@ fun List.diff(newList: List) = } fun BigDecimal.roundWhole() = this.setScale(0, RoundingMode.HALF_EVEN) + +fun defaultCoin(denom: String) = coin { + this.denom = denom + this.amount = "0" +} diff --git a/service/src/main/kotlin/io/provenance/explorer/domain/models/explorer/ParamModels.kt b/service/src/main/kotlin/io/provenance/explorer/domain/models/explorer/ParamModels.kt index db83e82a..ba3430c6 100644 --- a/service/src/main/kotlin/io/provenance/explorer/domain/models/explorer/ParamModels.kt +++ b/service/src/main/kotlin/io/provenance/explorer/domain/models/explorer/ParamModels.kt @@ -1,71 +1,72 @@ package io.provenance.explorer.domain.models.explorer import com.fasterxml.jackson.databind.JsonNode +import io.provenance.explorer.JSON_NODE_FACTORY data class Params( - val cosmos: CosmosParams, - val prov: ProvParams, + val cosmos: CosmosParams = CosmosParams(), + val prov: ProvParams = ProvParams(), ) data class CosmosParams( - val auth: JsonNode, - val bank: JsonNode, - val dist: DistParams, - val gov: GovParams, - val mint: MintParams, - val slashing: SlashingParams, - val staking: JsonNode, - val ibc: IBCParams, - val wasm: JsonNode + val auth: JsonNode = JSON_NODE_FACTORY.objectNode(), + val bank: JsonNode = JSON_NODE_FACTORY.objectNode(), + val dist: DistParams = DistParams(), + val gov: GovParams = GovParams(), + val mint: MintParams = MintParams(), + val slashing: SlashingParams = SlashingParams(), + val staking: JsonNode = JSON_NODE_FACTORY.objectNode(), + val ibc: IBCParams = IBCParams(), + val wasm: JsonNode = JSON_NODE_FACTORY.objectNode() ) data class DistParams( - val community_tax: String, - val base_proposer_reward: String, - val bonus_proposer_reward: String, - val withdraw_addr_enabled: Boolean, + val community_tax: String = "", + val base_proposer_reward: String = "", + val bonus_proposer_reward: String = "", + val withdraw_addr_enabled: Boolean = false, ) data class GovParams( - val voting: JsonNode, - val tallying: TallyingParams, - val deposit: JsonNode, + val voting: JsonNode = JSON_NODE_FACTORY.objectNode(), + val tallying: TallyingParams = TallyingParams(), + val deposit: JsonNode = JSON_NODE_FACTORY.objectNode(), ) data class TallyingParams( - val quorum: String, - val threshold: String, - val veto_threshold: String + val quorum: String = "", + val threshold: String = "", + val veto_threshold: String = "" ) data class MintParams( - val mint_denom: String, - val inflation_rate_change: String, - val inflation_max: String, - val inflation_min: String, - val goal_bonded: String, - val blocks_per_year: Long, + val mint_denom: String = "", + val inflation_rate_change: String = "", + val inflation_max: String = "", + val inflation_min: String = "", + val goal_bonded: String = "", + val blocks_per_year: Long = 0L, ) data class SlashingParams( - val signed_blocks_window: Long, - val min_signed_per_window: String, - val downtime_jail_duration: String, - val slash_fraction_double_sign: String, - val slash_fraction_downtime: String, + val signed_blocks_window: Long = 0L, + val min_signed_per_window: String = "", + val downtime_jail_duration: String = "", + val slash_fraction_double_sign: String = "", + val slash_fraction_downtime: String = "", ) data class IBCParams( - val transfer: JsonNode, - val client: JsonNode, - val icaController: JsonNode?, - val icaHost: JsonNode + val transfer: JsonNode = JSON_NODE_FACTORY.objectNode(), + val client: JsonNode = JSON_NODE_FACTORY.objectNode(), + val icaController: JsonNode = JSON_NODE_FACTORY.objectNode(), + val icaHost: JsonNode = JSON_NODE_FACTORY.objectNode() ) data class ProvParams( - val attribute: JsonNode, - val marker: JsonNode, - val metadata: JsonNode, - val name: JsonNode, - val msgFees: JsonNode + val attribute: JsonNode = JSON_NODE_FACTORY.objectNode(), + val marker: JsonNode = JSON_NODE_FACTORY.objectNode(), + val metadata: JsonNode = JSON_NODE_FACTORY.objectNode(), + val name: JsonNode = JSON_NODE_FACTORY.objectNode(), + val msgFees: JsonNode = JSON_NODE_FACTORY.objectNode() ) diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/extensions/Domain.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/extensions/Domain.kt index 37c94456..5b0871c4 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/extensions/Domain.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/extensions/Domain.kt @@ -54,6 +54,12 @@ fun getPagination(offset: Int, limit: Int) = this.countTotal = true } +fun getPaginationNoCount(offset: Int, limit: Int) = + pageRequest { + this.offset = offset.toLong() + this.limit = limit.toLong() + } + //endregion //region Marker Extensions diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AccountGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AccountGrpcClient.kt index 38e69644..b526b91e 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AccountGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AccountGrpcClient.kt @@ -3,24 +3,34 @@ package io.provenance.explorer.grpc.v1 import cosmos.auth.v1beta1.queryAccountRequest import cosmos.bank.v1beta1.Bank import cosmos.bank.v1beta1.queryAllBalancesRequest +import cosmos.bank.v1beta1.queryAllBalancesResponse import cosmos.bank.v1beta1.queryBalanceRequest import cosmos.bank.v1beta1.queryDenomMetadataRequest import cosmos.bank.v1beta1.queryDenomMetadataResponse import cosmos.bank.v1beta1.queryDenomOwnersRequest +import cosmos.bank.v1beta1.queryDenomOwnersResponse import cosmos.bank.v1beta1.queryDenomsMetadataRequest import cosmos.bank.v1beta1.queryParamsRequest import cosmos.bank.v1beta1.querySpendableBalancesRequest import cosmos.bank.v1beta1.querySupplyOfRequest import cosmos.base.v1beta1.CoinOuterClass -import cosmos.distribution.v1beta1.QueryOuterClass +import cosmos.distribution.v1beta1.queryCommunityPoolRequest import cosmos.distribution.v1beta1.queryDelegationTotalRewardsRequest +import cosmos.distribution.v1beta1.queryDelegationTotalRewardsResponse +import cosmos.staking.v1beta1.pool import cosmos.staking.v1beta1.queryDelegatorDelegationsRequest import cosmos.staking.v1beta1.queryDelegatorDelegationsResponse import cosmos.staking.v1beta1.queryDelegatorUnbondingDelegationsRequest +import cosmos.staking.v1beta1.queryDelegatorUnbondingDelegationsResponse import cosmos.staking.v1beta1.queryPoolRequest +import cosmos.staking.v1beta1.queryPoolResponse import cosmos.staking.v1beta1.queryRedelegationsRequest +import cosmos.staking.v1beta1.queryRedelegationsResponse import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor +import io.provenance.explorer.domain.extensions.defaultCoin import io.provenance.explorer.domain.extensions.toDecimalStringOld import io.provenance.explorer.grpc.extensions.addBlockHeightToQuery import io.provenance.explorer.grpc.extensions.getPagination @@ -73,14 +83,17 @@ class AccountGrpcClient(channelUri: URI) { } suspend fun getAccountBalances(address: String, offset: Int, limit: Int) = - bankClient.allBalances( - queryAllBalancesRequest { - this.address = address - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryAllBalancesResponse { } + else + bankClient.allBalances( + queryAllBalancesRequest { + this.address = address + this.pagination = getPagination(offset, limit) + } + ) suspend fun getAccountBalancesAll(address: String): MutableList { + if (UNDER_MAINTENANCE) return mutableListOf() var (offset, limit) = 0 to 100 val results = @@ -107,12 +120,14 @@ class AccountGrpcClient(channelUri: URI) { } suspend fun getAccountBalanceForDenom(address: String, denom: String) = - bankClient.balance( - queryBalanceRequest { - this.address = address - this.denom = denom - } - ).balance + if (UNDER_MAINTENANCE) defaultCoin(denom) + else + bankClient.balance( + queryBalanceRequest { + this.address = address + this.denom = denom + } + ).balance suspend fun getAccountBalancesAllAtHeight(address: String, height: Int): MutableList { var (offset, limit) = 0 to 100 @@ -155,6 +170,7 @@ class AccountGrpcClient(channelUri: URI) { ).balance suspend fun getSpendableBalancesAll(address: String): MutableList { + if (UNDER_MAINTENANCE) return mutableListOf() var (offset, limit) = 0 to 100 val results = @@ -181,6 +197,7 @@ class AccountGrpcClient(channelUri: URI) { } suspend fun getSpendableBalanceDenom(address: String, denom: String): CoinOuterClass.Coin? { + if (ExplorerProperties.UNDER_MAINTENANCE) return defaultCoin(denom) var (offset, limit) = 0 to 100 var balance: CoinOuterClass.Coin? var noInfo = false @@ -199,7 +216,8 @@ class AccountGrpcClient(channelUri: URI) { } suspend fun getCurrentSupply(denom: String) = - bankClient.supplyOf(querySupplyOfRequest { this.denom = denom }).amount + if (UNDER_MAINTENANCE) defaultCoin(denom) + else bankClient.supplyOf(querySupplyOfRequest { this.denom = denom }).amount suspend fun getDenomMetadata(denom: String) = try { @@ -209,6 +227,7 @@ class AccountGrpcClient(channelUri: URI) { } suspend fun getAllDenomMetadata(): MutableList { + if (UNDER_MAINTENANCE) return mutableListOf() var (offset, limit) = 0 to 100 val results = @@ -238,37 +257,55 @@ class AccountGrpcClient(channelUri: URI) { } suspend fun getUnbondingDelegations(address: String, offset: Int, limit: Int) = - stakingClient.delegatorUnbondingDelegations( - queryDelegatorUnbondingDelegationsRequest { - this.delegatorAddr = address - this.pagination = getPagination(offset, limit) - } - ) + try { + stakingClient.delegatorUnbondingDelegations( + queryDelegatorUnbondingDelegationsRequest { + this.delegatorAddr = address + this.pagination = getPagination(offset, limit) + } + ) + } catch (e: Exception) { + queryDelegatorUnbondingDelegationsResponse { } + } suspend fun getRedelegations(address: String, offset: Int, limit: Int) = - stakingClient.redelegations( - queryRedelegationsRequest { - this.delegatorAddr = address - this.pagination = getPagination(offset, limit) - } - ) + try { + stakingClient.redelegations( + queryRedelegationsRequest { + this.delegatorAddr = address + this.pagination = getPagination(offset, limit) + } + ) + } catch (e: Exception) { + queryRedelegationsResponse { } + } - suspend fun getStakingPool() = stakingClient.pool(queryPoolRequest { }) + suspend fun getStakingPool() = + if (UNDER_MAINTENANCE) queryPoolResponse { this.pool = pool { this.bondedTokens = "0" } } + else + stakingClient.pool(queryPoolRequest { }) suspend fun getRewards(delAddr: String) = - distClient.delegationTotalRewards(queryDelegationTotalRewardsRequest { this.delegatorAddress = delAddr }) + try { + distClient.delegationTotalRewards(queryDelegationTotalRewardsRequest { this.delegatorAddress = delAddr }) + } catch (e: Exception) { + queryDelegationTotalRewardsResponse { } + } suspend fun getCommunityPoolAmount(denom: String): String = - distClient.communityPool(QueryOuterClass.QueryCommunityPoolRequest.newBuilder().build()).poolList - .filter { it.denom == denom }[0]?.amount!!.toDecimalStringOld() + if (UNDER_MAINTENANCE) "0" + else + distClient.communityPool(queryCommunityPoolRequest { }).poolList.filter { it.denom == denom }[0]?.amount!!.toDecimalStringOld() suspend fun getMarkerBalance(address: String, denom: String): String = - bankClient.balance( - queryBalanceRequest { - this.address = address - this.denom = denom - } - ).balance.amount + if (UNDER_MAINTENANCE) "0" + else + bankClient.balance( + queryBalanceRequest { + this.address = address + this.denom = denom + } + ).balance.amount suspend fun getBankParams() = bankClient.params(queryParamsRequest { }) @@ -277,10 +314,14 @@ class AccountGrpcClient(channelUri: URI) { suspend fun getMintParams() = mintClient.params(cosmos.mint.v1beta1.queryParamsRequest { }) suspend fun getDenomHolders(denom: String, offset: Int, count: Int) = - bankClient.denomOwners( - queryDenomOwnersRequest { - this.denom = denom - this.pagination = getPagination(offset, count) - } - ) + try { + bankClient.denomOwners( + queryDenomOwnersRequest { + this.denom = denom + this.pagination = getPagination(offset, count) + } + ) + } catch (e: Exception) { + queryDenomOwnersResponse { } + } } diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AttributeGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AttributeGrpcClient.kt index b834be85..32d2d368 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AttributeGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AttributeGrpcClient.kt @@ -4,10 +4,12 @@ import io.grpc.ManagedChannelBuilder import io.provenance.attribute.v1.Attribute import io.provenance.attribute.v1.queryAttributesRequest import io.provenance.attribute.v1.queryParamsRequest +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import io.provenance.explorer.grpc.extensions.getPagination import io.provenance.name.v1.queryResolveRequest import io.provenance.name.v1.queryReverseLookupRequest +import io.provenance.name.v1.queryReverseLookupResponse import org.springframework.stereotype.Component import java.net.URI import java.util.concurrent.TimeUnit @@ -41,6 +43,7 @@ class AttributeGrpcClient(channelUri: URI) { } suspend fun getAllAttributesForAddress(address: String?): MutableList { + if (UNDER_MAINTENANCE) return mutableListOf() if (address == null) return mutableListOf() var (offset, limit) = 0 to 100 @@ -69,12 +72,14 @@ class AttributeGrpcClient(channelUri: URI) { } suspend fun getNamesForAddress(address: String, offset: Int, limit: Int) = - nameClient.reverseLookup( - queryReverseLookupRequest { - this.address = address - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryReverseLookupResponse { }.defaultInstanceForType + else + nameClient.reverseLookup( + queryReverseLookupRequest { + this.address = address + this.pagination = getPagination(offset, limit) + } + ) suspend fun getOwnerForName(name: String) = try { diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AuthzGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AuthzGrpcClient.kt index 960b113d..9dc6f551 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AuthzGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/AuthzGrpcClient.kt @@ -2,8 +2,12 @@ package io.provenance.explorer.grpc.v1 import cosmos.authz.v1beta1.QueryGrpcKt import cosmos.authz.v1beta1.queryGranteeGrantsRequest +import cosmos.authz.v1beta1.queryGranteeGrantsResponse import cosmos.authz.v1beta1.queryGranterGrantsRequest +import cosmos.authz.v1beta1.queryGranterGrantsResponse +import cosmos.base.query.v1beta1.pageResponse import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import io.provenance.explorer.grpc.extensions.getPagination import org.springframework.stereotype.Component @@ -35,18 +39,22 @@ class AuthzGrpcClient(channelUri: URI) { } suspend fun getGrantsForGranter(granter: String, offset: Int, limit: Int) = - authzClient.granterGrants( - queryGranterGrantsRequest { - this.granter = granter - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryGranterGrantsResponse { this.pagination = pageResponse { this.total = 0 } } + else + authzClient.granterGrants( + queryGranterGrantsRequest { + this.granter = granter + this.pagination = getPagination(offset, limit) + } + ) suspend fun getGrantsForGrantee(grantee: String, offset: Int, limit: Int) = - authzClient.granteeGrants( - queryGranteeGrantsRequest { - this.grantee = grantee - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryGranteeGrantsResponse { this.pagination = pageResponse { this.total = 0 } } + else + authzClient.granteeGrants( + queryGranteeGrantsRequest { + this.grantee = grantee + this.pagination = getPagination(offset, limit) + } + ) } diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/BlockGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/BlockGrpcClient.kt index 6ce36a82..dad25ec0 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/BlockGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/BlockGrpcClient.kt @@ -40,5 +40,10 @@ class BlockGrpcClient(channelUri: URI) { null } - suspend fun getLatestBlock() = tmClient.getLatestBlock(getLatestBlockRequest { }) + suspend fun getLatestBlock() = + try { + tmClient.getLatestBlock(getLatestBlockRequest { }) + } catch (e: Exception) { + null + } } diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/FeegrantGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/FeegrantGrpcClient.kt index 64f2519d..c29d6a9a 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/FeegrantGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/FeegrantGrpcClient.kt @@ -1,9 +1,13 @@ package io.provenance.explorer.grpc.v1 +import cosmos.base.query.v1beta1.pageResponse import cosmos.feegrant.v1beta1.QueryGrpcKt import cosmos.feegrant.v1beta1.queryAllowancesByGranterRequest +import cosmos.feegrant.v1beta1.queryAllowancesByGranterResponse import cosmos.feegrant.v1beta1.queryAllowancesRequest +import cosmos.feegrant.v1beta1.queryAllowancesResponse import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import io.provenance.explorer.grpc.extensions.getPagination import org.springframework.stereotype.Component @@ -35,18 +39,22 @@ class FeegrantGrpcClient(channelUri: URI) { } suspend fun getAllowancesForGrantee(grantee: String, offset: Int, limit: Int) = - feegrantClient.allowances( - queryAllowancesRequest { - this.grantee = grantee - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryAllowancesResponse { this.pagination = pageResponse { this.total = 0 } } + else + feegrantClient.allowances( + queryAllowancesRequest { + this.grantee = grantee + this.pagination = getPagination(offset, limit) + } + ) suspend fun getGrantsByGranter(granter: String, offset: Int, limit: Int) = - feegrantClient.allowancesByGranter( - queryAllowancesByGranterRequest { - this.granter = granter - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryAllowancesByGranterResponse { this.pagination = pageResponse { this.total = 0 } } + else + feegrantClient.allowancesByGranter( + queryAllowancesByGranterRequest { + this.granter = granter + this.pagination = getPagination(offset, limit) + } + ) } diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/GovGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/GovGrpcClient.kt index 36ef37da..ea3947af 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/GovGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/GovGrpcClient.kt @@ -1,13 +1,19 @@ package io.provenance.explorer.grpc.v1 import cosmos.auth.v1beta1.queryModuleAccountByNameRequest +import cosmos.gov.v1.depositParams import cosmos.gov.v1.queryParamsRequest +import cosmos.gov.v1.queryParamsResponse import cosmos.gov.v1.queryProposalRequest import cosmos.gov.v1.queryTallyResultRequest +import cosmos.gov.v1.tallyParams import cosmos.upgrade.v1beta1.queryAppliedPlanRequest import cosmos.upgrade.v1beta1.queryCurrentPlanRequest import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE +import io.provenance.explorer.config.ExplorerProperties.Companion.UTILITY_TOKEN import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor +import io.provenance.explorer.domain.extensions.defaultCoin import io.provenance.explorer.domain.models.explorer.GovParamType import io.provenance.explorer.grpc.extensions.addBlockHeightToQuery import org.springframework.stereotype.Component @@ -53,7 +59,18 @@ class GovGrpcClient(channelUri: URI) { } suspend fun getParams(param: GovParamType) = - govClient.params(queryParamsRequest { this.paramsType = param.name }) + if (UNDER_MAINTENANCE) + queryParamsResponse { + this.depositParams = depositParams { + this.minDeposit.add(defaultCoin(UTILITY_TOKEN)) + } + this.tallyParams = tallyParams { + this.quorum = "0" + this.threshold = "0" + this.vetoThreshold = "0" + } + } + else govClient.params(queryParamsRequest { this.paramsType = param.name }) suspend fun getParamsAtHeight(param: GovParamType, height: Int) = try { @@ -72,7 +89,11 @@ class GovGrpcClient(channelUri: URI) { } suspend fun getIfUpgradeApplied(planName: String) = - upgradeClient.appliedPlan(queryAppliedPlanRequest { this.name = planName }) + try { + upgradeClient.appliedPlan(queryAppliedPlanRequest { this.name = planName }) + } catch (e: Exception) { + null + } suspend fun getIfUpgradeScheduled() = try { diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/IbcGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/IbcGrpcClient.kt index d662e203..a7cca6d7 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/IbcGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/IbcGrpcClient.kt @@ -1,5 +1,6 @@ package io.provenance.explorer.grpc.v1 +import ibc.applications.transfer.v1.denomTrace import ibc.applications.transfer.v1.queryDenomTraceRequest import ibc.applications.transfer.v1.queryEscrowAddressRequest import ibc.applications.transfer.v1.queryParamsRequest @@ -7,6 +8,7 @@ import ibc.core.channel.v1.queryChannelClientStateRequest import ibc.core.channel.v1.queryChannelRequest import ibc.core.client.v1.queryClientParamsRequest import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import kotlinx.coroutines.runBlocking import org.springframework.stereotype.Component @@ -56,9 +58,11 @@ class IbcGrpcClient(channelUri: URI) { } suspend fun getDenomTrace(hash: String) = - transferClient.denomTrace( - queryDenomTraceRequest { this.hash = hash } - ).denomTrace + if (UNDER_MAINTENANCE) denomTrace { } + else + transferClient.denomTrace( + queryDenomTraceRequest { this.hash = hash } + ).denomTrace suspend fun getEscrowAddress(portId: String, channelId: String, hrpPrefix: String) = runBlocking { transferClient.escrowAddress( diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MetadataGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MetadataGrpcClient.kt index a4e3983e..0b130169 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MetadataGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MetadataGrpcClient.kt @@ -2,17 +2,21 @@ package io.provenance.explorer.grpc.v1 import cosmos.base.query.v1beta1.pageRequest import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import io.provenance.explorer.domain.extensions.toByteString -import io.provenance.explorer.grpc.extensions.getPagination +import io.provenance.explorer.grpc.extensions.getPaginationNoCount import io.provenance.metadata.v1.QueryGrpcKt.QueryCoroutineStub import io.provenance.metadata.v1.contractSpecificationRequest +import io.provenance.metadata.v1.contractSpecificationResponse import io.provenance.metadata.v1.ownershipRequest +import io.provenance.metadata.v1.ownershipResponse import io.provenance.metadata.v1.queryParamsRequest import io.provenance.metadata.v1.recordSpecificationRequest import io.provenance.metadata.v1.recordSpecificationsForContractSpecificationRequest import io.provenance.metadata.v1.scopeRequest import io.provenance.metadata.v1.scopeSpecificationRequest +import io.provenance.metadata.v1.scopeSpecificationResponse import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import org.springframework.stereotype.Component @@ -44,14 +48,16 @@ class MetadataGrpcClient(channelUri: URI, private val semaphore: Semaphore) { } suspend fun getScopesByOwner(address: String, offset: Int = 0, limit: Int = 10) = - semaphore.withPermit { - metadataClient.ownership( - ownershipRequest { - this.address = address - this.pagination = getPagination(offset, limit) - } - ) - } + if (UNDER_MAINTENANCE) ownershipResponse { } + else + semaphore.withPermit { + metadataClient.ownership( + ownershipRequest { + this.address = address + this.pagination = getPaginationNoCount(offset, limit) + } + ) + } suspend fun getScopesByOwnerTotal(address: String) = semaphore.withPermit { @@ -79,27 +85,35 @@ class MetadataGrpcClient(channelUri: URI, private val semaphore: Semaphore) { } suspend fun getScopeById(uuid: String, includeRecords: Boolean = false, includeSessions: Boolean = false) = - metadataClient.scope( - scopeRequest { - this.scopeId = uuid - this.includeRecords = includeRecords - this.includeSessions = includeSessions - } - ) + if (UNDER_MAINTENANCE) null + else + metadataClient.scope( + scopeRequest { + this.scopeId = uuid + this.includeRecords = includeRecords + this.includeSessions = includeSessions + } + ) suspend fun getScopeSpecById(addr: String) = - metadataClient.scopeSpecification(scopeSpecificationRequest { this.specificationId = addr }) + if (UNDER_MAINTENANCE) scopeSpecificationResponse { } + else + metadataClient.scopeSpecification(scopeSpecificationRequest { this.specificationId = addr }) suspend fun getContractSpecById(addr: String, includeRecords: Boolean = false) = - metadataClient.contractSpecification( - contractSpecificationRequest { - this.specificationId = addr - this.includeRecordSpecs = includeRecords - } - ) + if (UNDER_MAINTENANCE) contractSpecificationResponse { } + else + metadataClient.contractSpecification( + contractSpecificationRequest { + this.specificationId = addr + this.includeRecordSpecs = includeRecords + } + ) suspend fun getRecordSpecById(addr: String) = - metadataClient.recordSpecification(recordSpecificationRequest { this.specificationId = addr }) + if (UNDER_MAINTENANCE) recordSpecificationRequest { } + else + metadataClient.recordSpecification(recordSpecificationRequest { this.specificationId = addr }) suspend fun getRecordSpecsForContractSpec(contractSpec: String) = semaphore.withPermit { diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MsgFeeGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MsgFeeGrpcClient.kt index 937adbf6..692c5007 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MsgFeeGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/MsgFeeGrpcClient.kt @@ -1,12 +1,14 @@ package io.provenance.explorer.grpc.v1 import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.ExplorerProperties.Companion.UTILITY_TOKEN_DEFAULT_GAS_PRICE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import io.provenance.explorer.grpc.extensions.addBlockHeightToQuery import io.provenance.explorer.grpc.extensions.getPagination import io.provenance.msgfees.v1.QueryGrpcKt import io.provenance.msgfees.v1.queryAllMsgFeesRequest +import io.provenance.msgfees.v1.queryAllMsgFeesResponse import kotlinx.coroutines.runBlocking import org.springframework.stereotype.Component import java.net.URI @@ -37,11 +39,13 @@ class MsgFeeGrpcClient(channelUri: URI) { } fun getMsgFees(offset: Int = 0, limit: Int = 100) = runBlocking { - msgFeeClient.queryAllMsgFees( - queryAllMsgFeesRequest { - this.pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryAllMsgFeesResponse { } + else + msgFeeClient.queryAllMsgFees( + queryAllMsgFeesRequest { + this.pagination = getPagination(offset, limit) + } + ) } fun getMsgFeesAtHeight(height: Int, offset: Int = 0, limit: Int = 100) = runBlocking { diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/SmartContractGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/SmartContractGrpcClient.kt index bb6faa08..1ac71ff9 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/SmartContractGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/SmartContractGrpcClient.kt @@ -1,24 +1,20 @@ package io.provenance.explorer.grpc.v1 -import com.google.protobuf.util.JsonFormat import cosmwasm.wasm.v1.QueryGrpcKt import cosmwasm.wasm.v1.queryCodeRequest import cosmwasm.wasm.v1.queryContractHistoryRequest +import cosmwasm.wasm.v1.queryContractHistoryResponse import cosmwasm.wasm.v1.queryContractInfoRequest import cosmwasm.wasm.v1.queryParamsRequest import io.grpc.ManagedChannelBuilder -import io.provenance.explorer.config.ExplorerProperties +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import org.springframework.stereotype.Component import java.net.URI import java.util.concurrent.TimeUnit @Component -class SmartContractGrpcClient( - channelUri: URI, - private val protoParser: JsonFormat.Parser, - private val explorerProps: ExplorerProperties -) { +class SmartContractGrpcClient(channelUri: URI,) { private val smcClient: QueryGrpcKt.QueryCoroutineStub @@ -52,7 +48,9 @@ class SmartContractGrpcClient( smcClient.contractInfo(queryContractInfoRequest { this.address = contractAddr }) suspend fun getSmContractHistory(contractAddr: String) = - smcClient.contractHistory(queryContractHistoryRequest { this.address = contractAddr }) + if (UNDER_MAINTENANCE) queryContractHistoryResponse { } + else + smcClient.contractHistory(queryContractHistoryRequest { this.address = contractAddr }) suspend fun getWasmParams() = smcClient.params(queryParamsRequest { }) } diff --git a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/ValidatorGrpcClient.kt b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/ValidatorGrpcClient.kt index d290734f..d6f71993 100644 --- a/service/src/main/kotlin/io/provenance/explorer/grpc/v1/ValidatorGrpcClient.kt +++ b/service/src/main/kotlin/io/provenance/explorer/grpc/v1/ValidatorGrpcClient.kt @@ -1,5 +1,6 @@ package io.provenance.explorer.grpc.v1 +import cosmos.base.query.v1beta1.pageResponse import cosmos.base.tendermint.v1beta1.Query import cosmos.base.tendermint.v1beta1.ServiceGrpc import cosmos.base.tendermint.v1beta1.getLatestValidatorSetRequest @@ -7,17 +8,21 @@ import cosmos.base.tendermint.v1beta1.getValidatorSetByHeightRequest import cosmos.distribution.v1beta1.queryDelegatorWithdrawAddressRequest import cosmos.distribution.v1beta1.queryValidatorCommissionRequest import cosmos.distribution.v1beta1.queryValidatorOutstandingRewardsRequest +import cosmos.distribution.v1beta1.validatorAccumulatedCommission import cosmos.slashing.v1beta1.Slashing import cosmos.slashing.v1beta1.queryParamsRequest import cosmos.slashing.v1beta1.querySigningInfosRequest import cosmos.staking.v1beta1.Staking import cosmos.staking.v1beta1.queryDelegationRequest import cosmos.staking.v1beta1.queryValidatorDelegationsRequest +import cosmos.staking.v1beta1.queryValidatorDelegationsResponse import cosmos.staking.v1beta1.queryValidatorRequest import cosmos.staking.v1beta1.queryValidatorUnbondingDelegationsRequest +import cosmos.staking.v1beta1.queryValidatorUnbondingDelegationsResponse import cosmos.staking.v1beta1.queryValidatorsRequest import cosmos.staking.v1beta1.validator import io.grpc.ManagedChannelBuilder +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.interceptor.GrpcLoggingInterceptor import io.provenance.explorer.grpc.extensions.addBlockHeightToQuery import io.provenance.explorer.grpc.extensions.getPagination @@ -58,24 +63,27 @@ class ValidatorGrpcClient(channelUri: URI) { slashingClient = SlashingGrpc.newBlockingStub(channel) } - fun getLatestValidators(): Query.GetLatestValidatorSetResponse { + fun getLatestValidators(): Query.GetLatestValidatorSetResponse? { + if (UNDER_MAINTENANCE) return null val limit = getStakingParams().params.maxValidators return tmClient.getLatestValidatorSet( getLatestValidatorSetRequest { pagination = getPagination(0, limit) } ) } - fun getValidatorsAtHeight(height: Int): Query.GetValidatorSetByHeightResponse { + fun getValidatorsAtHeight(height: Long): Query.GetValidatorSetByHeightResponse? { + if (UNDER_MAINTENANCE) return null val limit = getStakingParams().params.maxValidators return tmClient.getValidatorSetByHeight( getValidatorSetByHeightRequest { - this.height = height.toLong() + this.height = height pagination = getPagination(0, limit) } ) } fun getStakingValidators(): MutableList { + if (UNDER_MAINTENANCE) return mutableListOf() var offset = 0 val limit = 100 @@ -109,12 +117,15 @@ class ValidatorGrpcClient(channelUri: URI) { } fun getStakingValidatorDelegations(address: String, offset: Int, limit: Int) = - stakingClient.validatorDelegations( - queryValidatorDelegationsRequest { - validatorAddr = address - pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) + queryValidatorDelegationsResponse { this.pagination = pageResponse { this.total = 0 } } + else + stakingClient.validatorDelegations( + queryValidatorDelegationsRequest { + validatorAddr = address + pagination = getPagination(offset, limit) + } + ) fun getValidatorSelfDelegations(valAddress: String, delAddress: String) = stakingClient.delegation( @@ -125,15 +136,19 @@ class ValidatorGrpcClient(channelUri: URI) { ) fun getStakingValidatorUnbondingDels(address: String, offset: Int, limit: Int) = - stakingClient.validatorUnbondingDelegations( - queryValidatorUnbondingDelegationsRequest { - validatorAddr = address - pagination = getPagination(offset, limit) - } - ) + if (UNDER_MAINTENANCE) queryValidatorUnbondingDelegationsResponse { } + else + stakingClient.validatorUnbondingDelegations( + queryValidatorUnbondingDelegationsRequest { + validatorAddr = address + pagination = getPagination(offset, limit) + } + ) fun getValidatorCommission(address: String) = - distClient.validatorCommission(queryValidatorCommissionRequest { validatorAddress = address }).commission + if (UNDER_MAINTENANCE) validatorAccumulatedCommission { } + else + distClient.validatorCommission(queryValidatorCommissionRequest { validatorAddress = address }).commission fun getValidatorRewards(address: String) = distClient.validatorOutstandingRewards( @@ -143,13 +158,16 @@ class ValidatorGrpcClient(channelUri: URI) { ).rewards fun getDelegatorWithdrawalAddress(delegator: String) = - distClient.delegatorWithdrawAddress( - queryDelegatorWithdrawAddressRequest { - delegatorAddress = delegator - } - ).withdrawAddress + if (UNDER_MAINTENANCE) null + else + distClient.delegatorWithdrawAddress( + queryDelegatorWithdrawAddressRequest { + delegatorAddress = delegator + } + ).withdrawAddress fun getSigningInfos(): MutableList { + if (UNDER_MAINTENANCE) return mutableListOf() var offset = 0 val limit = 100 @@ -165,7 +183,8 @@ class ValidatorGrpcClient(channelUri: URI) { return infos } - fun getSlashingParams() = slashingClient.params(queryParamsRequest { }) + fun getSlashingParams() = + if (UNDER_MAINTENANCE) null else slashingClient.params(queryParamsRequest { }) fun getDistParams() = distClient.params(cosmos.distribution.v1beta1.queryParamsRequest { }) diff --git a/service/src/main/kotlin/io/provenance/explorer/service/BlockService.kt b/service/src/main/kotlin/io/provenance/explorer/service/BlockService.kt index 610a0c10..a8f19980 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/BlockService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/BlockService.kt @@ -21,11 +21,13 @@ class BlockService(private val blockClient: BlockGrpcClient) { fun getLatestBlockHeightIndexOrFromChain() = getBlockIndexFromCache()?.maxHeightRead ?: getLatestBlockHeight() - fun getBlockAtHeightFromChain(height: Int) = runBlocking { blockClient.getBlockAtHeight(height) } + fun getBlockAtHeightFromChain(height: Int) = + runBlocking { blockClient.getBlockAtHeight(height) } ?: getBlockAtHeight(height)?.block fun getBlockAtHeight(height: Int) = transaction { BlockCacheRecord.findById(height) } - fun getLatestBlockHeight(): Int = runBlocking { blockClient.getLatestBlock().block.height() } + fun getLatestBlockHeight(): Int = + runBlocking { blockClient.getLatestBlock()?.block?.height() } ?: getMaxBlockCacheHeight() fun updateBlockMaxHeightIndex(maxHeightRead: Int) = BlockIndexRecord.save(maxHeightRead, null) diff --git a/service/src/main/kotlin/io/provenance/explorer/service/ExplorerService.kt b/service/src/main/kotlin/io/provenance/explorer/service/ExplorerService.kt index 8c8f4c9a..a1ddd3a6 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/ExplorerService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/ExplorerService.kt @@ -14,7 +14,9 @@ import io.provenance.explorer.VANILLA_MAPPER import io.provenance.explorer.config.ExplorerProperties import io.provenance.explorer.config.ExplorerProperties.Companion.PROV_ACC_PREFIX import io.provenance.explorer.config.ExplorerProperties.Companion.PROV_VAL_OPER_PREFIX +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.ExplorerProperties.Companion.UTILITY_TOKEN +import io.provenance.explorer.config.ResourceNotFoundException import io.provenance.explorer.domain.core.PREFIX_SCOPE import io.provenance.explorer.domain.core.logger import io.provenance.explorer.domain.entities.BlockCacheHourlyTxCountsRecord @@ -108,7 +110,7 @@ class ExplorerService( fun getBlockAtHeight(height: Int?) = runBlocking(Dispatchers.IO) { val queryHeight = height ?: (blockService.getMaxBlockCacheHeight() - 1) - val blockResponse = asyncV2.getBlock(queryHeight)!! + val blockResponse = asyncV2.getBlock(queryHeight) ?: throw ResourceNotFoundException("Invalid Height : $queryHeight") val nextBlock = asyncV2.getBlock(queryHeight + 1) val validatorsResponse = validatorService.getValidatorsByHeight(queryHeight) hydrateBlock(blockResponse, nextBlock, validatorsResponse) @@ -142,8 +144,8 @@ class ExplorerService( hash = blockResponse.blockId.hash.toHash(), time = blockResponse.block.header.time.formattedString(), proposerAddress = proposer.proposerOperatorAddress, - moniker = stakingValidator.moniker, - icon = stakingValidator.imageUrl, + moniker = stakingValidator?.moniker ?: "UNDER MAINTENANCE", + icon = stakingValidator?.imageUrl, votingPower = CountTotal( if (votingVals != null) validatorsResponse.validatorsList.filter { it.address in votingVals } @@ -235,7 +237,7 @@ class ExplorerService( one.getUpgradePlan()!!.name, one.getUpgradePlan()!!.info.getChainVersionFromUrl(props.upgradeVersionRegex), version, - runBlocking { govClient.getIfUpgradeApplied(one.getUpgradePlan()!!.name) }.height.toInt() != one.getUpgradePlan()!!.height.toInt(), + (runBlocking { govClient.getIfUpgradeApplied(one.getUpgradePlan()!!.name) }?.height?.toInt() ?: 0) != one.getUpgradePlan()!!.height.toInt(), scheduledName?.let { name -> name == one.getUpgradePlan()!!.name } ?: false, url ) @@ -306,6 +308,8 @@ class ExplorerService( ) fun getParams(): Params = runBlocking { + if (UNDER_MAINTENANCE) return@runBlocking Params() + val authParams = async { accountClient.getAuthParams().params } val bankParams = async { accountClient.getBankParams().params } val distParams = validatorClient.getDistParams().params @@ -313,7 +317,7 @@ class ExplorerService( val tallyParams = async { govClient.getParams(GovParamType.tallying).tallyParams } val depositParams = async { govClient.getParams(GovParamType.deposit).depositParams } val mintParams = async { accountClient.getMintParams().params } - val slashingParams = validatorClient.getSlashingParams().params + val slashingParams = validatorClient.getSlashingParams()!!.params val stakingParams = validatorClient.getStakingParams().params val transferParams = async { ibcClient.getTransferParams().params } val clientParams = async { ibcClient.getClientParams().params } @@ -373,8 +377,8 @@ class ExplorerService( val status = ValidatorState.ALL val valFilter = validatorSet.map { it.address } val stakingValidators = validatorService.getStakingValidators(status, valFilter, page.toOffset(count), count) - val votingSet = asyncV2.getBlock(height + 1)!! - .getVotingSet(Types.BlockIDFlag.BLOCK_ID_FLAG_ABSENT_VALUE).keys + val votingSet = asyncV2.getBlock(height + 1) + ?.getVotingSet(Types.BlockIDFlag.BLOCK_ID_FLAG_ABSENT_VALUE)?.keys ?: emptySet() val proposer = transaction { BlockProposerRecord.findById(height)!! } val results = validatorService.hydrateValidators(validatorSet, listOf(), stakingValidators, height.toLong()).map { diff --git a/service/src/main/kotlin/io/provenance/explorer/service/IbcService.kt b/service/src/main/kotlin/io/provenance/explorer/service/IbcService.kt index 95403c75..b2a5b0f5 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/IbcService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/IbcService.kt @@ -9,7 +9,6 @@ import ibc.core.channel.v1.Tx.MsgAcknowledgement import ibc.core.channel.v1.Tx.MsgRecvPacket import ibc.core.channel.v1.Tx.MsgTimeout import ibc.core.channel.v1.Tx.MsgTimeoutOnClose -import io.provenance.explorer.config.ExplorerProperties import io.provenance.explorer.config.ExplorerProperties.Companion.PROV_ACC_PREFIX import io.provenance.explorer.config.ResourceNotFoundException import io.provenance.explorer.domain.core.logger @@ -51,7 +50,6 @@ class IbcService( private val assetService: AssetService, private val accountService: AccountService, private val protoPrinter: JsonFormat.Printer, - private val props: ExplorerProperties ) { protected val logger = logger(IbcService::class) diff --git a/service/src/main/kotlin/io/provenance/explorer/service/NftService.kt b/service/src/main/kotlin/io/provenance/explorer/service/NftService.kt index 789f43be..58b64728 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/NftService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/NftService.kt @@ -1,6 +1,8 @@ package io.provenance.explorer.service import com.google.protobuf.util.JsonFormat +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE +import io.provenance.explorer.config.ResourceNotFoundException import io.provenance.explorer.domain.core.MdParent import io.provenance.explorer.domain.core.MetadataAddress import io.provenance.explorer.domain.core.getParentForType @@ -9,6 +11,7 @@ import io.provenance.explorer.domain.core.toMAddress import io.provenance.explorer.domain.core.toMAddressContractSpec import io.provenance.explorer.domain.core.toMAddressScope import io.provenance.explorer.domain.core.toMAddressScopeSpec +import io.provenance.explorer.domain.entities.AccountTokenCountRecord import io.provenance.explorer.domain.entities.NftContractSpecRecord import io.provenance.explorer.domain.entities.NftScopeRecord import io.provenance.explorer.domain.entities.NftScopeSpecRecord @@ -54,12 +57,13 @@ class NftService( fun getScopesForOwningAddress(address: String, page: Int, count: Int) = runBlocking { metadataClient.getScopesByOwner(address, page.toOffset(count), count).let { val records = it.scopeUuidsList.map { uuid -> scopeToListview(uuid) } - PagedResults(it.pagination.total.pageCountOfResults(count), records, it.pagination.total) + val total = transaction { AccountTokenCountRecord.findByAddress(address)?.nftCount ?: 0 }.toLong() + PagedResults(total.pageCountOfResults(count), records, total) } } private fun scopeToListview(addr: String) = runBlocking { - metadataClient.getScopeById(addr).let { + metadataClient.getScopeById(addr)!!.let { val lastTx = TxNftJoinRecord.findTxByUuid(it.scope.scopeIdInfo.scopeUuid, 0, 1).firstOrNull() ScopeListview( it.scope.scopeIdInfo.scopeUuid, @@ -75,7 +79,7 @@ class NftService( } fun getScopeDetail(addr: String) = runBlocking { - metadataClient.getScopeById(addr).let { + metadataClient.getScopeById(addr)?.let { val spec = getScopeDescrip(it.scope.scopeSpecIdInfo.scopeSpecAddr) val attributes = async { attrClient.getAllAttributesForAddress(it.scope.scopeIdInfo.scopeAddr) } ScopeDetail( @@ -89,12 +93,14 @@ class NftService( it.scope.scope.valueOwnerAddress, attributes.await().map { attr -> attr.toResponse() } ) - } + } ?: throw ResourceNotFoundException("Invalid Scope address : '$addr'") } fun getRecordsForScope(addr: String): List = runBlocking { + if (UNDER_MAINTENANCE) return@runBlocking listOf() // get scope val scope = metadataClient.getScopeById(addr, true, true) + ?: throw ResourceNotFoundException("Invalid Scope address : '$addr'") // get scope spec -> contract specs val scopeSpec = metadataClient.getScopeSpecById(scope.scope.scopeSpecIdInfo.scopeSpecAddr) val contractSpecs = scopeSpec.scopeSpecification.specification.contractSpecIdsList.map { it.toMAddress() }.toSet() diff --git a/service/src/main/kotlin/io/provenance/explorer/service/NotificationService.kt b/service/src/main/kotlin/io/provenance/explorer/service/NotificationService.kt index 565c5b73..2fbd6737 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/NotificationService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/NotificationService.kt @@ -50,7 +50,7 @@ class NotificationService( .filter { it.status == Gov.ProposalStatus.PROPOSAL_STATUS_PASSED.name } .filter { proposal -> val name = proposal.getUpgradePlan()!!.name - govClient.getIfUpgradeApplied(name).height == 0L && + (govClient.getIfUpgradeApplied(name)?.height ?: 0L) == 0L && proposal.getUpgradePlan()!!.height > currentHeight }.sortedBy { it.proposalId } diff --git a/service/src/main/kotlin/io/provenance/explorer/service/TokenService.kt b/service/src/main/kotlin/io/provenance/explorer/service/TokenService.kt index a880ac75..a4c7bff2 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/TokenService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/TokenService.kt @@ -11,6 +11,7 @@ import io.ktor.http.ContentType import io.provenance.explorer.KTOR_CLIENT_JAVA import io.provenance.explorer.VANILLA_MAPPER import io.provenance.explorer.config.ExplorerProperties.Companion.PROV_ACC_PREFIX +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE import io.provenance.explorer.config.ExplorerProperties.Companion.UTILITY_TOKEN import io.provenance.explorer.domain.core.logger import io.provenance.explorer.domain.entities.AccountRecord @@ -196,16 +197,18 @@ class TokenService(private val accountClient: AccountGrpcClient) { fun maxSupply() = runBlocking { accountClient.getCurrentSupply(UTILITY_TOKEN).amount.toBigDecimal() } // total supply = max - burned -> comes from the nhash marker address - fun totalSupply() = maxSupply() - burnedSupply().roundWhole() + fun totalSupply() = if (UNDER_MAINTENANCE) BigDecimal.ZERO else (maxSupply() - burnedSupply().roundWhole()) // circulating supply = max - burned - modules - zero seq - pool - nonspendable fun circulatingSupply() = - maxSupply() // max - .minus(burnedSupply()) // burned - .minus(totalBalanceForList(zeroSeqAccounts().toSet() + moduleAccounts().addressList())) // modules/zero seq - .minus(communityPoolSupply()) // pool - .minus(totalNonspendableBalanceForList(vestingAccounts().addressList())) // nonSpendable - .roundWhole() + if (UNDER_MAINTENANCE) BigDecimal.ZERO + else + maxSupply() // max + .minus(burnedSupply()) // burned + .minus(totalBalanceForList(zeroSeqAccounts().toSet() + moduleAccounts().addressList())) // modules/zero seq + .minus(communityPoolSupply()) // pool + .minus(totalNonspendableBalanceForList(vestingAccounts().addressList())) // nonSpendable + .roundWhole() // rich list = all accounts - nhash marker - zero seq - modules - contracts ->>>>>>>>> out of total fun richList(topCount: Int = 100) = transaction { diff --git a/service/src/main/kotlin/io/provenance/explorer/service/ValidatorService.kt b/service/src/main/kotlin/io/provenance/explorer/service/ValidatorService.kt index 5842e0e9..3694e76c 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/ValidatorService.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/ValidatorService.kt @@ -87,9 +87,15 @@ class ValidatorService( protected val logger = logger(ValidatorService::class) - fun getActiveSet() = grpcClient.getStakingParams().params.maxValidators + fun getActiveSet() = + try { + grpcClient.getStakingParams().params.maxValidators + } catch (e: Exception) { + 100 + } - fun getSlashingParams() = grpcClient.getSlashingParams().params + fun getSlashingParams() = grpcClient.getSlashingParams()?.params + fun getSignedBocksWindow() = getSlashingParams()?.signedBlocksWindow?.toBigInteger() ?: BigInteger.ZERO // Assumes that the returned validators are active at that height fun getValidatorsByHeight(blockHeight: Int) = transaction { @@ -99,7 +105,7 @@ class ValidatorService( } ?: throw ResourceNotFoundException("Invalid height: '$blockHeight'") fun buildValidatorsAtHeight(blockHeight: Int) = - grpcClient.getValidatorsAtHeight(blockHeight).let { ValidatorsCacheRecord.buildInsert(blockHeight, it) } + grpcClient.getValidatorsAtHeight(blockHeight.toLong())!!.let { ValidatorsCacheRecord.buildInsert(blockHeight, it) } // Gets a single staking validator from cache fun getStakingValidator(operatorAddress: String) = @@ -107,8 +113,8 @@ class ValidatorService( ?: saveValidator(operatorAddress) fun saveValidator(address: String, height: Int? = null) = runBlocking { - grpcClient.getStakingValidator(address, height) - .let { + grpcClient.getStakingValidatorOrNull(address, height) + ?.let { StakingValidatorCacheRecord.insertIgnore( address, it.operatorAddress.translateAddress().accountAddr, @@ -124,8 +130,8 @@ class ValidatorService( getImgUrl(it.description.identity) ?.let { img -> AddressImageRecord.upsert(it.operatorAddress, img) } } - }.also { ValidatorStateRecord.refreshCurrentStateView() } - .let { ValidatorStateRecord.findByOperator(getActiveSet(), address)!! } + }?.also { ValidatorStateRecord.refreshCurrentStateView() } + ?.let { ValidatorStateRecord.findByOperator(getActiveSet(), address)!! } } fun validateValidator(validator: String) = @@ -136,33 +142,32 @@ class ValidatorService( getValidatorOperatorAddress(address)?.let { addr -> val currentHeight = blockService.getLatestBlockHeight().toBigInteger() val signingInfo = getSigningInfos().firstOrNull { it.address == addr.consensusAddr } - val validatorSet = grpcClient.getLatestValidators().validatorsList + val validatorSet = getRecentValidatorsOrRaw().second val latestValidator = validatorSet.firstOrNull { it.address == addr.consensusAddr } val votingPowerTotal = validatorSet.sumOf { it.votingPower.toBigInteger() } - val slashingParams = getSlashingParams() validateStatus(addr, latestValidator, addr.operatorAddrId) .also { if (it) ValidatorStateRecord.refreshCurrentStateView() } val stakingValidator = getStakingValidator(addr.operatorAddress) ValidatorDetails( if (latestValidator != null) CountTotal(latestValidator.votingPower.toBigInteger(), votingPowerTotal) else null, - stakingValidator.json.description.moniker, + stakingValidator?.json?.description?.moniker ?: "", addr.operatorAddress, addr.accountAddr, - grpcClient.getDelegatorWithdrawalAddress(addr.accountAddr), + grpcClient.getDelegatorWithdrawalAddress(addr.accountAddr) ?: addr.accountAddr, addr.consensusAddr, - addr.consensusAddr.validatorMissedBlocks(slashingParams.signedBlocksWindow.toBigInteger(), currentHeight) + addr.consensusAddr.validatorMissedBlocks(getSignedBocksWindow(), currentHeight) .let { (mbCount, window) -> CountTotal(mbCount.toBigInteger(), window) }, signingInfo?.startHeight ?: currentHeight.toLong(), - addr.consensusAddr.validatorUptime(slashingParams.signedBlocksWindow.toBigInteger(), currentHeight), - stakingValidator.imageUrl, - stakingValidator.json.description.details, - stakingValidator.json.description.website, - stakingValidator.json.description.identity, - stakingValidator.currentState.toString().lowercase(), - if (stakingValidator.currentState != ACTIVE) stakingValidator.json.unbondingHeight else null, - if (stakingValidator.jailed) signingInfo?.jailedUntil?.toDateTime() else null, - stakingValidator.removed + addr.consensusAddr.validatorUptime(getSignedBocksWindow(), currentHeight), + stakingValidator?.imageUrl, + stakingValidator?.json?.description?.details, + stakingValidator?.json?.description?.website, + stakingValidator?.json?.description?.identity, + stakingValidator?.currentState?.toString()?.lowercase() ?: ACTIVE.toString().lowercase(), + if (stakingValidator?.currentState != ACTIVE) stakingValidator?.json?.unbondingHeight else null, + if (stakingValidator != null && stakingValidator.jailed) signingInfo?.jailedUntil?.toDateTime() else null, + stakingValidator?.removed ?: false ) } ?: throw ResourceNotFoundException("Invalid validator address: '$address'") @@ -263,6 +268,14 @@ class ValidatorService( PagedResults(recs.size.toLong().pageCountOfResults(recs.size), recs, recs.size.toLong()) } + private fun getRecentValidatorsOrRaw() = + grpcClient.getLatestValidators()?.let { it.blockHeight to it.validatorsList } + ?: ValidatorsCacheRecord.getLatestByHeight().let { it.blockHeight to it.validatorsList } + + private fun getValidatorsAtHeightOrRaw(height: Long) = + grpcClient.getValidatorsAtHeight(height.get24HrBlockHeight(cacheService.getAvgBlockTime()))?.validatorsList + ?: ValidatorsCacheRecord.getAtHeight(height).validatorsList + // In point to get most recent validators fun getRecentValidators(count: Int, page: Int, status: String) = aggregateValidatorsRecent(count, page, status) @@ -272,10 +285,8 @@ class ValidatorService( status: String ): PagedResults { val statusEnum = ValidatorState.valueOf(status.uppercase()) - val (height, validatorSet) = grpcClient.getLatestValidators().let { it.blockHeight to it.validatorsList } - val hr24ChangeSet = grpcClient.getValidatorsAtHeight( - height.get24HrBlockHeight(cacheService.getAvgBlockTime()).toInt() - ).validatorsList + val (height, validatorSet) = getRecentValidatorsOrRaw() + val hr24ChangeSet = getValidatorsAtHeightOrRaw(height) getStakingValidators(statusEnum).map { v -> validateStatus(v, validatorSet.firstOrNull { it.address == v.consensusAddr }, v.operatorAddrId) }.also { map -> if (map.contains(true)) ValidatorStateRecord.refreshCurrentStateView() } @@ -292,12 +303,11 @@ class ValidatorService( height: Long ) = let { val totalVotingPower = validatorSet.sumOf { it.votingPower.toBigInteger() } - val slashWindow = grpcClient.getSlashingParams().params.signedBlocksWindow.toBigInteger() stakingVals .map { stakingVal -> val validator = validatorSet.firstOrNull { it.address == stakingVal.consensusAddr } val hr24Validator = hr24ChangeSet.firstOrNull { it.address == stakingVal.consensusAddr } - hydrateValidator(validator, hr24Validator, stakingVal, totalVotingPower, height, slashWindow) + hydrateValidator(validator, hr24Validator, stakingVal, totalVotingPower, height, getSignedBocksWindow()) } } @@ -464,7 +474,7 @@ class ValidatorService( val signatures = lastBlock.signaturesList .map { it.validatorAddress.translateByteArray().consensusAccountAddr } val currentVals = ValidatorsCacheRecord.findById(lastBlock.height.toInt())?.validators - ?: grpcClient.getValidatorsAtHeight(lastBlock.height.toInt()) + ?: grpcClient.getValidatorsAtHeight(lastBlock.height)!! currentVals.validatorsList.forEach { vali -> if (!signatures.contains(vali.address)) @@ -583,9 +593,12 @@ class ValidatorService( fun activeValidatorUptimeStats() = transaction { // Get Parameters - val (window, slashedAtPercent) = getSlashingParams().let { it.signedBlocksWindow to it.minSignedPerWindow } - val slashedCount = - slashedAtPercent.toString(Charsets.UTF_8).toDecimal().multiply(BigDecimal(window)).toLong() + val (window, slashedAtPercent) = getSlashingParams() + .let { + (it?.signedBlocksWindow ?: 0L) to + (it?.minSignedPerWindow?.toString(Charsets.UTF_8) ?: "0.0") + } + val slashedCount = slashedAtPercent.toDecimal().multiply(BigDecimal(window)).toLong() // Height to count from val currentHeight = getLatestHeight() @@ -628,7 +641,7 @@ class ValidatorService( currentHeight.toLong(), window, slashedCount, - slashedAtPercent.toString(Charsets.UTF_8).toPercentageOld(), + slashedAtPercent.toPercentageOld(), avgUptime, (avgUptime / window.toDouble()).toPercentage(), records.sortedByDescending { it.missedCount } diff --git a/service/src/main/kotlin/io/provenance/explorer/service/async/AsyncCachingV2.kt b/service/src/main/kotlin/io/provenance/explorer/service/async/AsyncCachingV2.kt index ba9d1f9b..6696fc03 100644 --- a/service/src/main/kotlin/io/provenance/explorer/service/async/AsyncCachingV2.kt +++ b/service/src/main/kotlin/io/provenance/explorer/service/async/AsyncCachingV2.kt @@ -396,7 +396,7 @@ class AsyncCachingV2( when (addrPair.first) { TxAddressJoinType.OPERATOR.name -> if (addrPair.second == null) { - validatorService.saveValidator(addr) + validatorService.saveValidator(addr)!! .let { pairCopy = addrPair.copy(second = it.operatorAddrId) } } TxAddressJoinType.ACCOUNT.name -> accountService.saveAccount(addr) diff --git a/service/src/main/kotlin/io/provenance/explorer/web/HelperController.kt b/service/src/main/kotlin/io/provenance/explorer/web/HelperController.kt new file mode 100644 index 00000000..0f82be9e --- /dev/null +++ b/service/src/main/kotlin/io/provenance/explorer/web/HelperController.kt @@ -0,0 +1,29 @@ +package io.provenance.explorer.web + +import io.provenance.explorer.config.ExplorerProperties.Companion.UNDER_MAINTENANCE +import io.swagger.annotations.Api +import io.swagger.annotations.ApiOperation +import io.swagger.v3.oas.annotations.Hidden +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.validation.annotation.Validated +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Validated +@RestController +@RequestMapping(path = ["/api"], produces = [MediaType.APPLICATION_JSON_VALUE]) +@Api( + value = "Helper controller", produces = "application/json", consumes = "application/json", tags = ["Helper"], + description = "This contains helper apis for infrastructure" +) +@Hidden +class HelperController { + + @ApiOperation("Returns true if node is under maintenance, else false") + @GetMapping("/maintenance_mode") + fun getMaintenanceMode() = ResponseEntity.ok(UnderMaintenance(UNDER_MAINTENANCE)) +} + +data class UnderMaintenance(val isUnderMaintenance: Boolean) diff --git a/service/src/main/resources/application-container.properties b/service/src/main/resources/application-container.properties index fa6c85e7..44991917 100644 --- a/service/src/main/resources/application-container.properties +++ b/service/src/main/resources/application-container.properties @@ -22,3 +22,4 @@ explorer.utility-token=${EXPLORER_UTILITY_TOKEN} explorer.utility-token-default-gas-price=${EXPLORER_UTILITY_TOKEN_DEFAULT_GAS_PRICE} explorer.utility-token-base-decimal-places=${EXPLORER_UTILITY_TOKEN_BASE_DECIMAL_PLACES} explorer.voting-power-padding=${EXPLORER_VOTING_POWER_PADDING} +explorer.is-under-maintenance=${EXPLORER_IS_UNDER_MAINTENANCE} diff --git a/service/src/main/resources/application-development.properties b/service/src/main/resources/application-development.properties index 53ce166b..6272fd69 100644 --- a/service/src/main/resources/application-development.properties +++ b/service/src/main/resources/application-development.properties @@ -20,6 +20,7 @@ explorer.utility-token-default-gas-price=1905 explorer.utility-token-base-decimal-places=9 ## Used to format the voting power; should reflect the difference of zeroes between the voting power setup and base denom explorer.voting-power-padding=1000000 +explorer.is-under-maintenance=false #### MAINNET SETTINGS