From d7500395cdabd656982022ff0a95350e07df0617 Mon Sep 17 00:00:00 2001 From: avkr003 Date: Fri, 28 Jan 2022 15:35:59 +0530 Subject: [PATCH 1/2] Adding Date class RFC3339 standard --- .run/explorer.run.xml | 12 +- app/constants/View.scala | 1 + app/models/Abstract/FeeGrant.scala | 5 +- app/models/blockchain/Authorization.scala | 9 +- app/models/blockchain/Block.scala | 18 +-- app/models/blockchain/Proposal.scala | 23 +--- app/models/blockchain/Redelegation.scala | 9 +- app/models/blockchain/Transaction.scala | 11 +- app/models/blockchain/Undelegation.scala | 15 +-- app/models/blockchain/Validator.scala | 11 +- app/models/common/FeeGrant.scala | 33 +++-- app/models/common/ProposalContents.scala | 3 +- app/models/common/Serializable.scala | 11 +- app/models/common/TransactionMessages.scala | 7 +- app/queries/Abstract/TendermintEvidence.scala | 8 +- .../responses/blockchain/BlockResponse.scala | 24 ++-- .../blockchain/GenesisResponse.scala | 9 +- .../blockchain/ProposalResponse.scala | 7 +- .../blockchain/TransactionResponse.scala | 3 +- app/queries/responses/common/FeeGrant.scala | 5 +- app/queries/responses/common/Header.scala | 5 +- .../responses/common/ProposalContents.scala | 3 +- .../responses/common/Redelegation.scala | 9 +- .../responses/common/SigningInfo.scala | 3 +- .../common/TransactionMessageResponses.scala | 10 +- .../responses/common/Undelegation.scala | 7 +- app/queries/responses/common/Validator.scala | 9 +- app/services/Block.scala | 18 +-- app/services/Startup.scala | 5 +- app/utilities/Blockchain.scala | 3 + app/utilities/Date.scala | 121 +++++++++++------- app/views/base/commonRFC3339Info.scala.html | 16 +++ .../accountDelegationsRedelegating.scala.html | 2 +- .../accountDelegationsUndelegating.scala.html | 2 +- .../accountTransactionsPerPage.scala.html | 2 +- .../blockchain/block/blockDetails.scala.html | 8 +- .../blockchain/block/blockListPage.scala.html | 2 +- .../blockchain/feeGrantAllowance.scala.html | 20 ++- .../blockchain/latestBlockHeight.scala.html | 5 +- .../proposal/proposalDetails.scala.html | 12 +- .../proposal/proposalList.scala.html | 4 +- .../transaction/transactionDetails.scala.html | 8 +- .../transactionListPage.scala.html | 2 +- .../cosmos/grantAuthorization.scala.html | 5 +- .../cosmos/submitEvidence.scala.html | 2 +- .../validatorTransactionsPerPage.scala.html | 2 +- conf/messages.en | 1 + 47 files changed, 276 insertions(+), 234 deletions(-) create mode 100644 app/views/base/commonRFC3339Info.scala.html diff --git a/.run/explorer.run.xml b/.run/explorer.run.xml index 1df0fbbee..2fbf1e1da 100644 --- a/.run/explorer.run.xml +++ b/.run/explorer.run.xml @@ -8,15 +8,15 @@ - - - - + + + + - + @@ -29,7 +29,7 @@ - + diff --git a/app/constants/View.scala b/app/constants/View.scala index 59936da1e..e95c50fa8 100644 --- a/app/constants/View.scala +++ b/app/constants/View.scala @@ -546,6 +546,7 @@ object View { val PERIOD_RESET = "PERIOD_RESET" val ALLOWED_MESSAGES = "ALLOWED_MESSAGES" val STAKE_AUTHORIZATION_TYPE = "STAKE_AUTHORIZATION_TYPE" + val NO_EXPIRY_DATE = "NO_EXPIRY_DATE" val AuthzAuthorizationMap: Map[String, String] = Map( constants.Blockchain.Authz.SEND_AUTHORIZATION -> "SEND_AUTHORIZATION", diff --git a/app/models/Abstract/FeeGrant.scala b/app/models/Abstract/FeeGrant.scala index ce948da87..ad3565986 100644 --- a/app/models/Abstract/FeeGrant.scala +++ b/app/models/Abstract/FeeGrant.scala @@ -3,12 +3,13 @@ package models.Abstract import models.common.FeeGrant.{AllowedMsgAllowance, BasicAllowance, PeriodicAllowance} import models.common.Serializable.Coin import play.api.libs.json.{Json, Writes} +import utilities.Date.RFC3339 object FeeGrant { abstract class FeeAllowance { - def getExpiration: Option[String] + def getExpiration: Option[RFC3339] - def validate(blockTime: String, fees: Seq[Coin]): (Boolean, FeeAllowance) + def deleteAndUpdate(blockTime: RFC3339, fees: Seq[Coin]): (Boolean, FeeAllowance) } implicit val feeAllowanceWrites: Writes[FeeAllowance] = { diff --git a/app/models/blockchain/Authorization.scala b/app/models/blockchain/Authorization.scala index e2a84ae33..9d1d5308b 100644 --- a/app/models/blockchain/Authorization.scala +++ b/app/models/blockchain/Authorization.scala @@ -10,13 +10,14 @@ import play.api.db.slick.DatabaseConfigProvider import play.api.libs.json.Json import queries.responses.common.Header import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 import java.sql.Timestamp import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} -case class Authorization(granter: String, grantee: String, msgTypeURL: String, grantedAuthorization: Authz.Authorization, expiration: String, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged +case class Authorization(granter: String, grantee: String, msgTypeURL: String, grantedAuthorization: Authz.Authorization, expiration: RFC3339, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged @Singleton class Authorizations @Inject()( @@ -37,10 +38,10 @@ class Authorizations @Inject()( private[models] val authorizationTable = TableQuery[AuthorizationTable] case class AuthorizationSerialized(granter: String, grantee: String, msgTypeURL: String, grantedAuthorization: String, expiration: String, createdBy: Option[String], createdOn: Option[Timestamp], createdOnTimeZone: Option[String], updatedBy: Option[String], updatedOn: Option[Timestamp], updatedOnTimeZone: Option[String]) { - def deserialize: Authorization = Authorization(granter = granter, grantee = grantee, msgTypeURL = msgTypeURL, grantedAuthorization = utilities.JSON.convertJsonStringToObject[Authz.Authorization](grantedAuthorization), expiration = expiration, createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) + def deserialize: Authorization = Authorization(granter = granter, grantee = grantee, msgTypeURL = msgTypeURL, grantedAuthorization = utilities.JSON.convertJsonStringToObject[Authz.Authorization](grantedAuthorization), expiration = RFC3339(expiration), createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) } - def serialize(authorization: Authorization): AuthorizationSerialized = AuthorizationSerialized(granter = authorization.granter, grantee = authorization.grantee, msgTypeURL = authorization.msgTypeURL, grantedAuthorization = Json.toJson(authorization.grantedAuthorization).toString, expiration = authorization.expiration, createdBy = authorization.createdBy, createdOn = authorization.createdOn, createdOnTimeZone = authorization.createdOnTimeZone, updatedBy = authorization.updatedBy, updatedOn = authorization.updatedOn, updatedOnTimeZone = authorization.updatedOnTimeZone) + def serialize(authorization: Authorization): AuthorizationSerialized = AuthorizationSerialized(granter = authorization.granter, grantee = authorization.grantee, msgTypeURL = authorization.msgTypeURL, grantedAuthorization = Json.toJson(authorization.grantedAuthorization).toString, expiration = authorization.expiration.toString, createdBy = authorization.createdBy, createdOn = authorization.createdOn, createdOnTimeZone = authorization.createdOnTimeZone, updatedBy = authorization.updatedBy, updatedOn = authorization.updatedOn, updatedOnTimeZone = authorization.updatedOnTimeZone) private def add(authorization: Authorization): Future[String] = db.run((authorizationTable returning authorizationTable.map(_.granter) += serialize(authorization)).asTry).map { case Success(result) => result @@ -105,7 +106,7 @@ class Authorizations @Inject()( object Service { - def create(granter: String, grantee: String, msgTypeURL: String, grantedAuthorization: Authz.Authorization, expiration: String): Future[String] = add(Authorization(granter = granter, grantee = grantee, msgTypeURL = msgTypeURL, grantedAuthorization = grantedAuthorization, expiration = expiration)) + def create(granter: String, grantee: String, msgTypeURL: String, grantedAuthorization: Authz.Authorization, expiration: RFC3339): Future[String] = add(Authorization(granter = granter, grantee = grantee, msgTypeURL = msgTypeURL, grantedAuthorization = grantedAuthorization, expiration = expiration)) def tryGet(granter: String, grantee: String, msgTypeURL: String): Future[Authorization] = findByGranterGranteeAndMsgType(granter = granter, grantee = grantee, msgTypeURL = msgTypeURL).map(_.deserialize) diff --git a/app/models/blockchain/Block.scala b/app/models/blockchain/Block.scala index 6903eaaf6..efcc4a5f1 100644 --- a/app/models/blockchain/Block.scala +++ b/app/models/blockchain/Block.scala @@ -1,20 +1,20 @@ package models.blockchain -import java.sql.Timestamp import exceptions.BaseException -import javax.inject.{Inject, Singleton} import models.Trait.Logged import org.postgresql.util.PSQLException import play.api.db.slick.DatabaseConfigProvider import play.api.libs.json.Json import play.api.{Configuration, Logger} import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 -import java.time.Duration +import java.sql.Timestamp +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} -case class Block(height: Int, time: String, proposerAddress: String, validators: Seq[String], createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged +case class Block(height: Int, time: RFC3339, proposerAddress: String, validators: Seq[String], createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged @Singleton class Blocks @Inject()( @@ -41,10 +41,10 @@ class Blocks @Inject()( private[models] val blockTable = TableQuery[BlockTable] case class BlockSerialized(height: Int, time: String, proposerAddress: String, validators: String, createdBy: Option[String], createdOn: Option[Timestamp], createdOnTimeZone: Option[String], updatedBy: Option[String], updatedOn: Option[Timestamp], updatedOnTimeZone: Option[String]) { - def deserialize: Block = Block(height = height, time = time, proposerAddress = proposerAddress, validators = utilities.JSON.convertJsonStringToObject[Seq[String]](validators), createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) + def deserialize: Block = Block(height = height, time = RFC3339(time), proposerAddress = proposerAddress, validators = utilities.JSON.convertJsonStringToObject[Seq[String]](validators), createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) } - def serialize(block: Block): BlockSerialized = BlockSerialized(height = block.height, time = block.time, proposerAddress = block.proposerAddress, validators = Json.toJson(block.validators).toString, createdBy = block.createdBy, createdOn = block.createdOn, createdOnTimeZone = block.createdOnTimeZone, updatedBy = block.updatedBy, updatedOn = block.updatedOn, updatedOnTimeZone = block.updatedOnTimeZone) + def serialize(block: Block): BlockSerialized = BlockSerialized(height = block.height, time = block.time.toString, proposerAddress = block.proposerAddress, validators = Json.toJson(block.validators).toString, createdBy = block.createdBy, createdOn = block.createdOn, createdOnTimeZone = block.createdOnTimeZone, updatedBy = block.updatedBy, updatedOn = block.updatedOn, updatedOnTimeZone = block.updatedOnTimeZone) private def add(block: Block): Future[String] = db.run((blockTable returning blockTable.map(_.height) += serialize(block)).asTry).map { case Success(result) => result.toString @@ -117,9 +117,9 @@ class Blocks @Inject()( object Service { - def create(height: Int, time: String, proposerAddress: String, validators: Seq[String]): Future[String] = add(Block(height = height, time = time, proposerAddress = proposerAddress, validators = validators)) + def create(height: Int, time: RFC3339, proposerAddress: String, validators: Seq[String]): Future[String] = add(Block(height = height, time = time, proposerAddress = proposerAddress, validators = validators)) - def insertOrUpdate(height: Int, time: String, proposerAddress: String, validators: Seq[String]): Future[Int] = upsert(Block(height = height, time = time, proposerAddress = proposerAddress, validators = validators)) + def insertOrUpdate(height: Int, time: RFC3339, proposerAddress: String, validators: Seq[String]): Future[Int] = upsert(Block(height = height, time = time, proposerAddress = proposerAddress, validators = validators)) def tryGet(height: Int): Future[Block] = tryGetBlockByHeight(height).map(_.deserialize) @@ -164,7 +164,7 @@ class Blocks @Inject()( (for { lastBlock <- lastBlock firstBlock <- getFirstBlock(lastBlock) - } yield utilities.NumericOperation.roundOff(Duration.between(utilities.Date.bcTimestampToZonedDateTime(lastBlock.time), utilities.Date.bcTimestampToZonedDateTime(firstBlock.time)).abs().toSeconds.toDouble / (lastBlock.height - firstBlock.height)) + } yield utilities.NumericOperation.roundOff(lastBlock.time.difference(firstBlock.time).abs().toSeconds.toDouble / (lastBlock.height - firstBlock.height)) ).recover { case baseException: BaseException => throw baseException } diff --git a/app/models/blockchain/Proposal.scala b/app/models/blockchain/Proposal.scala index 92a4d3202..128335c5c 100644 --- a/app/models/blockchain/Proposal.scala +++ b/app/models/blockchain/Proposal.scala @@ -3,10 +3,8 @@ package models.blockchain import exceptions.BaseException import models.Abstract.ProposalContent import models.Trait.Logged -import models.common.Parameters.{GovernanceParameter, MintingParameter} -import models.common.ProposalContents._ import models.common.Serializable.{Coin, FinalTallyResult} -import models.common.TransactionMessages.{Deposit, SubmitProposal} +import models.common.TransactionMessages.SubmitProposal import org.postgresql.util.PSQLException import play.api.db.slick.DatabaseConfigProvider import play.api.libs.json.Json @@ -15,13 +13,14 @@ import queries.blockchain.GetProposal import queries.responses.blockchain.ProposalResponse.{Response => ProposalResponse} import queries.responses.common.Header import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 import java.sql.Timestamp import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} -case class Proposal(id: Int, content: ProposalContent, status: String, finalTallyResult: FinalTallyResult, submitTime: String, depositEndTime: String, totalDeposit: Seq[Coin], votingStartTime: String, votingEndTime: String, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged { +case class Proposal(id: Int, content: ProposalContent, status: String, finalTallyResult: FinalTallyResult, submitTime: RFC3339, depositEndTime: RFC3339, totalDeposit: Seq[Coin], votingStartTime: RFC3339, votingEndTime: RFC3339, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged { def addDeposit(depositCoins: Seq[Coin]): Proposal = Proposal( id = id, content = content, status = status, finalTallyResult = finalTallyResult, submitTime = submitTime, depositEndTime = depositEndTime, @@ -45,13 +44,13 @@ case class Proposal(id: Int, content: ProposalContent, status: String, finalTall true } - def activateVotingPeriod(currentTime: String, votingPeriod: Long): Proposal = Proposal( + def activateVotingPeriod(currentTime: RFC3339, votingPeriod: Long): Proposal = Proposal( id = id, content = content, status = constants.Blockchain.Proposal.Status.VOTING_PERIOD, finalTallyResult = finalTallyResult, submitTime = submitTime, depositEndTime = depositEndTime, totalDeposit = totalDeposit, votingStartTime = currentTime, - votingEndTime = utilities.Date.addTime(currentTime, votingPeriod) + votingEndTime = currentTime.addEpoch(votingPeriod) ) def isPassed: Boolean = status match { @@ -67,10 +66,6 @@ class Proposals @Inject()( getProposal: GetProposal, configuration: Configuration, utilitiesOperations: utilities.Operations, - blockchainParameters: Parameters, - blockchainBalances: Balances, - blockchainValidators: Validators, - blockchainTokens: Tokens, )(implicit executionContext: ExecutionContext) { val databaseConfig = databaseConfigProvider.get[JdbcProfile] @@ -88,10 +83,10 @@ class Proposals @Inject()( private[models] val proposalTable = TableQuery[ProposalTable] case class ProposalSerialized(id: Int, content: String, status: String, finalTallyResult: String, submitTime: String, depositEndTime: String, totalDeposit: String, votingStartTime: String, votingEndTime: String, createdBy: Option[String], createdOn: Option[Timestamp], createdOnTimeZone: Option[String], updatedBy: Option[String], updatedOn: Option[Timestamp], updatedOnTimeZone: Option[String]) { - def deserialize: Proposal = Proposal(id = id, content = utilities.JSON.convertJsonStringToObject[ProposalContent](content), status = status, finalTallyResult = utilities.JSON.convertJsonStringToObject[FinalTallyResult](finalTallyResult), submitTime = submitTime, depositEndTime = depositEndTime, totalDeposit = utilities.JSON.convertJsonStringToObject[Seq[Coin]](totalDeposit), votingStartTime = votingStartTime, votingEndTime = votingEndTime, createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) + def deserialize: Proposal = Proposal(id = id, content = utilities.JSON.convertJsonStringToObject[ProposalContent](content), status = status, finalTallyResult = utilities.JSON.convertJsonStringToObject[FinalTallyResult](finalTallyResult), submitTime = RFC3339(submitTime), depositEndTime = RFC3339(depositEndTime), totalDeposit = utilities.JSON.convertJsonStringToObject[Seq[Coin]](totalDeposit), votingStartTime = RFC3339(votingStartTime), votingEndTime = RFC3339(votingEndTime), createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) } - def serialize(proposal: Proposal): ProposalSerialized = ProposalSerialized(id = proposal.id, content = Json.toJson(proposal.content).toString, status = proposal.status, finalTallyResult = Json.toJson(proposal.finalTallyResult).toString, submitTime = proposal.submitTime, depositEndTime = proposal.depositEndTime, totalDeposit = Json.toJson(proposal.totalDeposit).toString, votingStartTime = proposal.votingStartTime, votingEndTime = proposal.votingEndTime, createdBy = proposal.createdBy, createdOn = proposal.createdOn, createdOnTimeZone = proposal.createdOnTimeZone, updatedBy = proposal.updatedBy, updatedOn = proposal.updatedOn, updatedOnTimeZone = proposal.updatedOnTimeZone) + def serialize(proposal: Proposal): ProposalSerialized = ProposalSerialized(id = proposal.id, content = Json.toJson(proposal.content).toString, status = proposal.status, finalTallyResult = Json.toJson(proposal.finalTallyResult).toString, submitTime = proposal.submitTime.toString, depositEndTime = proposal.depositEndTime.toString, totalDeposit = Json.toJson(proposal.totalDeposit).toString, votingStartTime = proposal.votingStartTime.toString, votingEndTime = proposal.votingEndTime.toString, createdBy = proposal.createdBy, createdOn = proposal.createdOn, createdOnTimeZone = proposal.createdOnTimeZone, updatedBy = proposal.updatedBy, updatedOn = proposal.updatedOn, updatedOnTimeZone = proposal.updatedOnTimeZone) private def add(proposal: Proposal): Future[Int] = db.run((proposalTable returning proposalTable.map(_.id) += serialize(proposal)).asTry).map { case Success(result) => result @@ -172,10 +167,6 @@ class Proposals @Inject()( def getLatestProposalID: Future[Int] = getMaxProposalID - def getAllActiveProposals(time: String): Future[Seq[Proposal]] = getAllProposals.map(_.filter(x => x.votingEndTime != "" && utilities.Date.isMature(completionTimestamp = x.votingEndTime, currentTimeStamp = time)).map(_.deserialize)) - - def getAllInactiveProposals(time: String): Future[Seq[Proposal]] = getAllProposals.map(_.filter(x => x.depositEndTime != "" && utilities.Date.isMature(completionTimestamp = x.depositEndTime, currentTimeStamp = time)).map(_.deserialize)) - def delete(id: Int): Future[Int] = deleteByID(id) def get(): Future[Seq[Proposal]] = getAllProposals.map(_.map(_.deserialize)) diff --git a/app/models/blockchain/Redelegation.scala b/app/models/blockchain/Redelegation.scala index cc0eef4fb..0b94cc2c4 100644 --- a/app/models/blockchain/Redelegation.scala +++ b/app/models/blockchain/Redelegation.scala @@ -17,6 +17,7 @@ import queries.responses.blockchain.ValidatorDelegatorDelegationResponse.{Respon import queries.responses.blockchain.DelegatorRedelegationsResponse.{Response => DelegatorRedelegationsResponse} import queries.responses.common.Header import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 import utilities.MicroNumber import scala.concurrent.{ExecutionContext, Future} @@ -174,11 +175,11 @@ class Redelegations @Inject()( } } - def onRedelegationCompletionEvent(delegator: String, srcValidator: String, dstValidator: String, currentBlockTimeStamp: String): Future[Unit] = { + def onRedelegationCompletionEvent(delegator: String, srcValidator: String, dstValidator: String, currentBlockTimeStamp: RFC3339): Future[Unit] = { val redelegation = Service.tryGet(delegatorAddress = delegator, validatorSourceAddress = srcValidator, validatorDestinationAddress = dstValidator) def updateOrDelete(redelegation: Redelegation) = { - val updatedEntries = redelegation.entries.filter(entry => !utilities.Date.isMature(completionTimestamp = entry.completionTime, currentTimeStamp = currentBlockTimeStamp)) + val updatedEntries = redelegation.entries.filterNot(_.isMature(currentBlockTimeStamp)) if (updatedEntries.isEmpty) Service.delete(delegatorAddress = redelegation.delegatorAddress, validatorSourceAddress = srcValidator, validatorDestinationAddress = dstValidator) else Service.insertOrUpdate(redelegation.copy(entries = updatedEntries)) } @@ -191,14 +192,14 @@ class Redelegations @Inject()( } } - def slashRedelegation(redelegation: Redelegation, infractionHeight: Int, currentBlockTIme: String, slashingFraction: BigDecimal): Future[MicroNumber] = { + def slashRedelegation(redelegation: Redelegation, infractionHeight: Int, currentBlockTIme: RFC3339, slashingFraction: BigDecimal): Future[MicroNumber] = { val delegation = blockchainDelegations.Service.get(delegatorAddress = redelegation.delegatorAddress, operatorAddress = redelegation.validatorDestinationAddress) val destinationValidator = blockchainValidators.Service.tryGet(redelegation.validatorDestinationAddress) def update(optionalDelegation: Option[Delegation], destinationValidator: Validator) = optionalDelegation.fold(Future(MicroNumber.zero))(delegation => { val updateEntries = utilitiesOperations.traverse(redelegation.entries)(entry => { val sharesToUnbond = slashingFraction * entry.sharesDestination - val unbond = if (entry.creationHeight >= infractionHeight && !utilities.Date.isMature(completionTimestamp = entry.completionTime, currentTimeStamp = currentBlockTIme) && sharesToUnbond != 0) { + val unbond = if (entry.creationHeight >= infractionHeight && !entry.isMature(currentBlockTIme) && sharesToUnbond != 0) { val slashShares = if (sharesToUnbond > delegation.shares) delegation.shares else sharesToUnbond blockchainUndelegations.Utility.unbond(delegation, destinationValidator, slashShares) } else Future(MicroNumber.zero) diff --git a/app/models/blockchain/Transaction.scala b/app/models/blockchain/Transaction.scala index 4cfcd16fb..14f08535d 100644 --- a/app/models/blockchain/Transaction.scala +++ b/app/models/blockchain/Transaction.scala @@ -9,6 +9,7 @@ import play.api.db.slick.DatabaseConfigProvider import play.api.libs.json.Json import play.api.{Configuration, Logger} import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 import java.sql.Timestamp import javax.inject.{Inject, Singleton} @@ -16,7 +17,7 @@ import scala.collection.immutable.ListMap import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} -case class Transaction(hash: String, height: Int, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: Seq[StdMsg], fee: Fee, memo: String, timestamp: String, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged { +case class Transaction(hash: String, height: Int, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: Seq[StdMsg], fee: Fee, memo: String, timestamp: RFC3339, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged { def status: Boolean = code == 0 @@ -59,10 +60,10 @@ class Transactions @Inject()( private[models] val transactionTable = TableQuery[TransactionTable] case class TransactionSerialized(hash: String, height: Int, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: String, fee: String, memo: String, timestamp: String, createdBy: Option[String], createdOn: Option[Timestamp], createdOnTimeZone: Option[String], updatedBy: Option[String], updatedOn: Option[Timestamp], updatedOnTimeZone: Option[String]) { - def deserialize: Transaction = Transaction(hash = hash, height = height, code = code, rawLog = rawLog, gasWanted = gasWanted, gasUsed = gasUsed, messages = utilities.JSON.convertJsonStringToObject[Seq[StdMsg]](messages), fee = utilities.JSON.convertJsonStringToObject[Fee](fee), memo = memo, timestamp = timestamp, createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) + def deserialize: Transaction = Transaction(hash = hash, height = height, code = code, rawLog = rawLog, gasWanted = gasWanted, gasUsed = gasUsed, messages = utilities.JSON.convertJsonStringToObject[Seq[StdMsg]](messages), fee = utilities.JSON.convertJsonStringToObject[Fee](fee), memo = memo, timestamp = RFC3339(timestamp), createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) } - def serialize(transaction: Transaction): TransactionSerialized = TransactionSerialized(hash = transaction.hash, height = transaction.height, code = transaction.code, rawLog = transaction.rawLog, gasWanted = transaction.gasWanted, gasUsed = transaction.gasUsed, messages = Json.toJson(transaction.messages).toString, fee = Json.toJson(transaction.fee).toString, memo = transaction.memo, timestamp = transaction.timestamp, createdBy = transaction.createdBy, createdOn = transaction.createdOn, createdOnTimeZone = transaction.createdOnTimeZone, updatedBy = transaction.updatedBy, updatedOn = transaction.updatedOn, updatedOnTimeZone = transaction.updatedOnTimeZone) + def serialize(transaction: Transaction): TransactionSerialized = TransactionSerialized(hash = transaction.hash, height = transaction.height, code = transaction.code, rawLog = transaction.rawLog, gasWanted = transaction.gasWanted, gasUsed = transaction.gasUsed, messages = Json.toJson(transaction.messages).toString, fee = Json.toJson(transaction.fee).toString, memo = transaction.memo, timestamp = transaction.timestamp.toString, createdBy = transaction.createdBy, createdOn = transaction.createdOn, createdOnTimeZone = transaction.createdOnTimeZone, updatedBy = transaction.updatedBy, updatedOn = transaction.updatedOn, updatedOnTimeZone = transaction.updatedOnTimeZone) private def add(transaction: Transaction): Future[Int] = db.run((transactionTable returning transactionTable.map(_.height) += serialize(transaction)).asTry).map { case Success(result) => result @@ -168,11 +169,11 @@ class Transactions @Inject()( object Service { - def create(hash: String, height: String, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: Seq[StdMsg], fee: Fee, memo: String, timestamp: String): Future[Int] = add(Transaction(hash = hash, height = height.toInt, code = code, rawLog = rawLog, gasWanted = gasWanted, gasUsed = gasUsed, messages = messages, fee = fee, memo = memo, timestamp = timestamp)) + def create(hash: String, height: String, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: Seq[StdMsg], fee: Fee, memo: String, timestamp: RFC3339): Future[Int] = add(Transaction(hash = hash, height = height.toInt, code = code, rawLog = rawLog, gasWanted = gasWanted, gasUsed = gasUsed, messages = messages, fee = fee, memo = memo, timestamp = timestamp)) def insertMultiple(transactions: Seq[Transaction]): Future[Seq[Int]] = addMultiple(transactions) - def insertOrUpdate(hash: String, height: String, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: Seq[StdMsg], fee: Fee, memo: String, timestamp: String): Future[Int] = upsert(Transaction(hash = hash, height = height.toInt, code = code, rawLog = rawLog, gasWanted = gasWanted, gasUsed = gasUsed, messages = messages, fee = fee, memo = memo, timestamp = timestamp)) + def insertOrUpdate(hash: String, height: String, code: Int, rawLog: String, gasWanted: String, gasUsed: String, messages: Seq[StdMsg], fee: Fee, memo: String, timestamp: RFC3339): Future[Int] = upsert(Transaction(hash = hash, height = height.toInt, code = code, rawLog = rawLog, gasWanted = gasWanted, gasUsed = gasUsed, messages = messages, fee = fee, memo = memo, timestamp = timestamp)) def tryGet(hash: String): Future[Transaction] = tryGetTransactionByHash(hash).map(_.deserialize) diff --git a/app/models/blockchain/Undelegation.scala b/app/models/blockchain/Undelegation.scala index 7730ec39f..8ff7fe261 100644 --- a/app/models/blockchain/Undelegation.scala +++ b/app/models/blockchain/Undelegation.scala @@ -1,10 +1,7 @@ package models.blockchain -import java.sql.Timestamp import akka.actor.ActorSystem import exceptions.BaseException - -import javax.inject.{Inject, Singleton} import models.Trait.Logged import models.common.Serializable.UndelegationEntry import models.common.TransactionMessages.Undelegate @@ -13,12 +10,14 @@ import play.api.db.slick.DatabaseConfigProvider import play.api.libs.json.Json import play.api.{Configuration, Logger} import queries.blockchain.{GetAllValidatorUndelegations, GetValidatorDelegatorUndelegation} -import queries.responses.blockchain.AllValidatorUndelegationsResponse.{Response => AllValidatorUndelegationsResponse} import queries.responses.blockchain.ValidatorDelegatorUndelegationResponse.{Response => ValidatorDelegatorUndelegationResponse} import queries.responses.common.Header import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 import utilities.MicroNumber +import java.sql.Timestamp +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} @@ -165,12 +164,12 @@ class Undelegations @Inject()( } } - def onUnbondingCompletionEvent(delegatorAddress: String, validatorAddress: String, currentBlockTimeStamp: String): Future[Unit] = { + def onUnbondingCompletionEvent(delegatorAddress: String, validatorAddress: String, currentBlockTimeStamp: RFC3339): Future[Unit] = { val updateBalance = blockchainBalances.Utility.insertOrUpdateBalance(delegatorAddress) val undelegation = Service.tryGet(delegatorAddress = delegatorAddress, validatorAddress = validatorAddress) def updateOrDelete(undelegation: Undelegation) = { - val updatedEntries = undelegation.entries.filter(entry => !utilities.Date.isMature(completionTimestamp = entry.completionTime, currentTimeStamp = currentBlockTimeStamp)) + val updatedEntries = undelegation.entries.filterNot(_.isMature(currentBlockTimeStamp)) if (updatedEntries.isEmpty) Service.delete(delegatorAddress = undelegation.delegatorAddress, validatorAddress = undelegation.validatorAddress) else Service.insertOrUpdate(undelegation.copy(entries = updatedEntries)) } @@ -184,11 +183,11 @@ class Undelegations @Inject()( } } - def slashUndelegation(undelegation: Undelegation, currentBlockTime: String, infractionHeight: Int, slashingFraction: BigDecimal): Future[Unit] = { + def slashUndelegation(undelegation: Undelegation, currentBlockTime: RFC3339, infractionHeight: Int, slashingFraction: BigDecimal): Future[Unit] = { val updatedEntries = undelegation.entries.map(entry => { val slashAmount = MicroNumber((slashingFraction * BigDecimal(entry.initialBalance.value)).toBigInt()) val unbondingSlashAmount = if (slashAmount < entry.balance) slashAmount else entry.balance - if (entry.creationHeight >= infractionHeight && !utilities.Date.isMature(completionTimestamp = entry.completionTime, currentTimeStamp = currentBlockTime) && unbondingSlashAmount != MicroNumber.zero) { + if (entry.creationHeight >= infractionHeight && !entry.isMature(currentBlockTime) && unbondingSlashAmount != MicroNumber.zero) { entry.copy(balance = entry.balance - unbondingSlashAmount) } else entry }) diff --git a/app/models/blockchain/Validator.scala b/app/models/blockchain/Validator.scala index b09478318..d625b923e 100644 --- a/app/models/blockchain/Validator.scala +++ b/app/models/blockchain/Validator.scala @@ -10,10 +10,10 @@ import org.postgresql.util.PSQLException import play.api.db.slick.DatabaseConfigProvider import play.api.libs.json.Json import play.api.{Configuration, Logger} -import models.common.PublicKeys._ import queries.blockchain.{GetBondedValidators, GetValidator} import queries.responses.common.Header import slick.jdbc.JdbcProfile +import utilities.Date.RFC3339 import utilities.MicroNumber import java.sql.Timestamp @@ -21,7 +21,7 @@ import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} -case class Validator(operatorAddress: String, hexAddress: String, consensusPublicKey: PublicKey, jailed: Boolean, status: String, tokens: MicroNumber, delegatorShares: BigDecimal, description: Description, unbondingHeight: Int, unbondingTime: String, commission: Commission, minimumSelfDelegation: MicroNumber, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged { +case class Validator(operatorAddress: String, hexAddress: String, consensusPublicKey: PublicKey, jailed: Boolean, status: String, tokens: MicroNumber, delegatorShares: BigDecimal, description: Description, unbondingHeight: Int, unbondingTime: RFC3339, commission: Commission, minimumSelfDelegation: MicroNumber, createdBy: Option[String] = None, createdOn: Option[Timestamp] = None, createdOnTimeZone: Option[String] = None, updatedBy: Option[String] = None, updatedOn: Option[Timestamp] = None, updatedOnTimeZone: Option[String] = None) extends Logged { def getTokensFromShares(shares: BigDecimal): MicroNumber = MicroNumber(((shares * BigDecimal(tokens.value)) / delegatorShares).toBigInt()) @@ -41,6 +41,7 @@ case class Validator(operatorAddress: String, hexAddress: String, consensusPubli def isBonded: Boolean = status == constants.Blockchain.ValidatorStatus.BONED + def isUnbondingMatured(currentTime: RFC3339): Boolean = !unbondingTime.isAfter(currentTime) } @Singleton @@ -70,10 +71,10 @@ class Validators @Inject()( private[models] val validatorTable = TableQuery[ValidatorTable] case class ValidatorSerialized(operatorAddress: String, hexAddress: String, consensusPublicKey: String, jailed: Boolean, status: String, tokens: String, delegatorShares: BigDecimal, description: String, unbondingHeight: Int, unbondingTime: String, commission: String, minimumSelfDelegation: String, createdBy: Option[String], createdOn: Option[Timestamp], createdOnTimeZone: Option[String], updatedBy: Option[String], updatedOn: Option[Timestamp], updatedOnTimeZone: Option[String]) { - def deserialize: Validator = Validator(operatorAddress = operatorAddress, hexAddress = hexAddress, consensusPublicKey = utilities.JSON.convertJsonStringToObject[PublicKey](consensusPublicKey), status = status, jailed = jailed, tokens = new MicroNumber(tokens), delegatorShares = delegatorShares, description = utilities.JSON.convertJsonStringToObject[Description](description), unbondingHeight = unbondingHeight, unbondingTime = unbondingTime, commission = utilities.JSON.convertJsonStringToObject[Commission](commission), minimumSelfDelegation = new MicroNumber(minimumSelfDelegation), createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) + def deserialize: Validator = Validator(operatorAddress = operatorAddress, hexAddress = hexAddress, consensusPublicKey = utilities.JSON.convertJsonStringToObject[PublicKey](consensusPublicKey), status = status, jailed = jailed, tokens = new MicroNumber(tokens), delegatorShares = delegatorShares, description = utilities.JSON.convertJsonStringToObject[Description](description), unbondingHeight = unbondingHeight, unbondingTime = RFC3339(unbondingTime), commission = utilities.JSON.convertJsonStringToObject[Commission](commission), minimumSelfDelegation = new MicroNumber(minimumSelfDelegation), createdBy = createdBy, createdOn = createdOn, createdOnTimeZone = createdOnTimeZone, updatedBy = updatedBy, updatedOn = updatedOn, updatedOnTimeZone = updatedOnTimeZone) } - def serialize(validator: Validator): ValidatorSerialized = ValidatorSerialized(operatorAddress = validator.operatorAddress, hexAddress = validator.hexAddress, consensusPublicKey = Json.toJson(validator.consensusPublicKey).toString, status = validator.status, jailed = validator.jailed, tokens = validator.tokens.toString, delegatorShares = validator.delegatorShares, description = Json.toJson(validator.description).toString, unbondingHeight = validator.unbondingHeight, unbondingTime = validator.unbondingTime, commission = Json.toJson(validator.commission).toString, minimumSelfDelegation = validator.minimumSelfDelegation.toString, createdBy = validator.createdBy, createdOn = validator.createdOn, createdOnTimeZone = validator.createdOnTimeZone, updatedBy = validator.updatedBy, updatedOn = validator.updatedOn, updatedOnTimeZone = validator.updatedOnTimeZone) + def serialize(validator: Validator): ValidatorSerialized = ValidatorSerialized(operatorAddress = validator.operatorAddress, hexAddress = validator.hexAddress, consensusPublicKey = Json.toJson(validator.consensusPublicKey).toString, status = validator.status, jailed = validator.jailed, tokens = validator.tokens.toString, delegatorShares = validator.delegatorShares, description = Json.toJson(validator.description).toString, unbondingHeight = validator.unbondingHeight, unbondingTime = validator.unbondingTime.toString, commission = Json.toJson(validator.commission).toString, minimumSelfDelegation = validator.minimumSelfDelegation.toString, createdBy = validator.createdBy, createdOn = validator.createdOn, createdOnTimeZone = validator.createdOnTimeZone, updatedBy = validator.updatedBy, updatedOn = validator.updatedOn, updatedOnTimeZone = validator.updatedOnTimeZone) private def add(validator: Validator): Future[String] = db.run((validatorTable returning validatorTable.map(_.operatorAddress) += serialize(validator)).asTry).map { case Success(result) => result @@ -373,7 +374,7 @@ class Validators @Inject()( val unbondingValidators = Service.getAllUnbondingValidatorList def checkAndUpdateUnbondingValidators(unbondingValidators: Seq[Validator]) = utilitiesOperations.traverse(unbondingValidators)(unbondingValidator => { - if (header.height >= unbondingValidator.unbondingHeight && utilities.Date.isMature(completionTimestamp = unbondingValidator.unbondingTime, currentTimeStamp = header.time)) { + if (header.height >= unbondingValidator.unbondingHeight && unbondingValidator.isUnbondingMatured(header.time)) { val updateOrDeleteValidator = if (unbondingValidator.delegatorShares == 0) Service.delete(unbondingValidator.operatorAddress) else Service.insertOrUpdate(unbondingValidator.copy(status = constants.Blockchain.ValidatorStatus.UNBONDED)) val withdrawValidatorRewards = blockchainWithdrawAddresses.Utility.withdrawRewards(utilities.Bech32.convertOperatorAddressToAccountAddress(unbondingValidator.operatorAddress)) diff --git a/app/models/common/FeeGrant.scala b/app/models/common/FeeGrant.scala index 3f6ec4033..93b48a542 100644 --- a/app/models/common/FeeGrant.scala +++ b/app/models/common/FeeGrant.scala @@ -6,6 +6,7 @@ import play.api.Logger import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json._ import utilities.Blockchain.{FeeGrant => utilitiesFeeGrant} +import utilities.Date.RFC3339 object FeeGrant { @@ -14,8 +15,8 @@ object FeeGrant { private implicit val logger: Logger = Logger(this.getClass) case class Allowance(allowanceType: String, value: AbstarctFeeGrant.FeeAllowance) { - def validate(blockTime: String, fees: Seq[Coin]): utilitiesFeeGrant.ValidateResponse = { - val (delete, updatedAllowanceValue) = this.value.validate(blockTime, fees) + def validate(blockTime: RFC3339, fees: Seq[Coin]): utilitiesFeeGrant.ValidateResponse = { + val (delete, updatedAllowanceValue) = this.value.deleteAndUpdate(blockTime, fees) utilitiesFeeGrant.ValidateResponse(delete = delete, updated = this.copy(value = updatedAllowanceValue)) } } @@ -27,12 +28,11 @@ object FeeGrant { implicit val allowanceWrites: Writes[Allowance] = Json.writes[Allowance] - case class BasicAllowance(spendLimit: Seq[Coin], expiration: Option[String]) extends AbstarctFeeGrant.FeeAllowance { - def getExpiration: Option[String] = expiration + case class BasicAllowance(spendLimit: Seq[Coin], expiration: Option[RFC3339]) extends AbstarctFeeGrant.FeeAllowance { + def getExpiration: Option[RFC3339] = expiration - def validate(blockTime: String, fees: Seq[Coin]): (Boolean, AbstarctFeeGrant.FeeAllowance) = { - if (getExpiration.nonEmpty && utilities.Date.isBefore(t1 = getExpiration.getOrElse(""), t2 = blockTime)) - (true, this) + def deleteAndUpdate(blockTime: RFC3339, fees: Seq[Coin]): (Boolean, AbstarctFeeGrant.FeeAllowance) = { + if (this.getExpiration.fold(false)(_.isBefore(blockTime))) (true, this) else if (spendLimit.nonEmpty) { val (left, _) = utilities.Blockchain.subtractCoins(spendLimit, fees) (left.exists(_.isZero), this.copy(spendLimit = left)) @@ -44,19 +44,18 @@ object FeeGrant { implicit val basicAllowanceWrites: Writes[BasicAllowance] = Json.writes[BasicAllowance] - case class PeriodicAllowance(basicAllowance: BasicAllowance, period: String, periodSpendLimit: Seq[Coin], periodCanSpend: Seq[Coin], periodReset: String) extends AbstarctFeeGrant.FeeAllowance { - def getExpiration: Option[String] = basicAllowance.getExpiration + case class PeriodicAllowance(basicAllowance: BasicAllowance, period: String, periodSpendLimit: Seq[Coin], periodCanSpend: Seq[Coin], periodReset: RFC3339) extends AbstarctFeeGrant.FeeAllowance { + def getExpiration: Option[RFC3339] = basicAllowance.getExpiration - def validate(blockTime: String, fees: Seq[Coin]): (Boolean, AbstarctFeeGrant.FeeAllowance) = { - if (getExpiration.nonEmpty && utilities.Date.isAfter(t1 = blockTime, t2 = getExpiration.getOrElse(""))) - (true, this) + def deleteAndUpdate(blockTime: RFC3339, fees: Seq[Coin]): (Boolean, AbstarctFeeGrant.FeeAllowance) = { + if (getExpiration.fold(false)(_.isBefore(blockTime))) (true, this) else { - val (resetPeriodCanSpend, updatedPeriodReset) = if (!utilities.Date.isBefore(t1 = blockTime, t2 = this.periodReset)) { + val (resetPeriodCanSpend, updatedPeriodReset) = if (!blockTime.isBefore(this.periodReset)) { val (_, isNeg) = utilities.Blockchain.subtractCoins(fromCoins = this.basicAllowance.spendLimit, amount = this.periodSpendLimit) val resetPeriodCanSpend = if (isNeg && this.basicAllowance.spendLimit.nonEmpty) this.basicAllowance.spendLimit else this.periodSpendLimit val updatedPeriodReset = { - val addPeriod = utilities.Date.addTime(timestamp = this.periodReset, addEpochTime = utilities.Date.getEpoch(this.period)) - if (utilities.Date.isAfter(t1 = blockTime, t2 = addPeriod)) utilities.Date.addTime(blockTime, utilities.Date.getEpoch(this.period)) else addPeriod + val addPeriod = blockTime.addEpoch(utilities.Date.getEpoch(this.period)) + if (blockTime.isAfter(addPeriod)) blockTime.addEpoch(utilities.Date.getEpoch(this.period)) else addPeriod } (resetPeriodCanSpend, updatedPeriodReset) } else (this.periodCanSpend, this.periodReset) @@ -74,9 +73,9 @@ object FeeGrant { implicit val periodicAllowanceWrites: Writes[PeriodicAllowance] = Json.writes[PeriodicAllowance] case class AllowedMsgAllowance(allowance: Allowance, allowedMessages: Seq[String]) extends AbstarctFeeGrant.FeeAllowance { - def getExpiration: Option[String] = allowance.value.getExpiration + def getExpiration: Option[RFC3339] = allowance.value.getExpiration - def validate(blockTime: String, fees: Seq[Coin]): (Boolean, AbstarctFeeGrant.FeeAllowance) = this.allowance.value.validate(blockTime, fees) + def deleteAndUpdate(blockTime: RFC3339, fees: Seq[Coin]): (Boolean, AbstarctFeeGrant.FeeAllowance) = this.allowance.value.deleteAndUpdate(blockTime, fees) } implicit val allowedMsgAllowanceReads: Reads[AllowedMsgAllowance] = Json.reads[AllowedMsgAllowance] diff --git a/app/models/common/ProposalContents.scala b/app/models/common/ProposalContents.scala index 55be93ca1..cebc6681b 100644 --- a/app/models/common/ProposalContents.scala +++ b/app/models/common/ProposalContents.scala @@ -6,6 +6,7 @@ import models.common.Serializable.Coin import play.api.Logger import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json.{JsObject, JsPath, Json, OWrites, Reads, Writes} +import utilities.Date.RFC3339 object ProposalContents { @@ -13,7 +14,7 @@ object ProposalContents { private implicit val logger: Logger = Logger(this.getClass) - case class Plan(name: String, time: String, height: String, info: String) + case class Plan(name: String, time: RFC3339, height: String, info: String) implicit val plainReads: Reads[Plan] = Json.reads[Plan] diff --git a/app/models/common/Serializable.scala b/app/models/common/Serializable.scala index 39c0d4e80..08ad424a8 100644 --- a/app/models/common/Serializable.scala +++ b/app/models/common/Serializable.scala @@ -7,6 +7,7 @@ import models.common.DataValue._ import play.api.Logger import play.api.libs.functional.syntax._ import play.api.libs.json._ +import utilities.Date.RFC3339 import utilities.MicroNumber object Serializable { @@ -35,7 +36,7 @@ object Serializable { implicit val commissionRatesReads: Reads[CommissionRates] = Json.reads[CommissionRates] - case class Commission(commissionRates: CommissionRates, updateTime: String) + case class Commission(commissionRates: CommissionRates, updateTime: RFC3339) implicit val commissionWrites: OWrites[Commission] = Json.writes[Commission] @@ -96,13 +97,17 @@ object Serializable { implicit val notificationTemplateWrites: OWrites[NotificationTemplate] = Json.writes[NotificationTemplate] - case class RedelegationEntry(creationHeight: Int, completionTime: String, initialBalance: MicroNumber, sharesDestination: BigDecimal) + case class RedelegationEntry(creationHeight: Int, completionTime: RFC3339, initialBalance: MicroNumber, sharesDestination: BigDecimal) { + def isMature(currentTime: RFC3339): Boolean = !this.completionTime.isAfter(currentTime) + } implicit val redelegationEntryReads: Reads[RedelegationEntry] = Json.reads[RedelegationEntry] implicit val redelegationEntryWrites: OWrites[RedelegationEntry] = Json.writes[RedelegationEntry] - case class UndelegationEntry(creationHeight: Int, completionTime: String, initialBalance: MicroNumber, balance: MicroNumber) + case class UndelegationEntry(creationHeight: Int, completionTime: RFC3339, initialBalance: MicroNumber, balance: MicroNumber) { + def isMature(currentTime: RFC3339): Boolean = !this.completionTime.isAfter(currentTime) + } implicit val undelegationEntryReads: Reads[UndelegationEntry] = Json.reads[UndelegationEntry] diff --git a/app/models/common/TransactionMessages.scala b/app/models/common/TransactionMessages.scala index caec707b5..10f4730dd 100644 --- a/app/models/common/TransactionMessages.scala +++ b/app/models/common/TransactionMessages.scala @@ -6,6 +6,7 @@ import models.common.Serializable._ import play.api.Logger import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json._ +import utilities.Date.RFC3339 import utilities.MicroNumber object TransactionMessages { @@ -35,7 +36,7 @@ object TransactionMessages { implicit val createVestingAccountWrites: OWrites[CreateVestingAccount] = Json.writes[CreateVestingAccount] //authz - case class Grant(authorization: Authz.Authorization, expiration: String) + case class Grant(authorization: Authz.Authorization, expiration: RFC3339) implicit val grantReads: Reads[Grant] = Json.reads[Grant] @@ -136,13 +137,13 @@ object TransactionMessages { implicit val fundCommunityPoolWrites: OWrites[FundCommunityPool] = Json.writes[FundCommunityPool] - case class Equivocation(height: Int, time: String, power: String, consensusAddress: String) + //evidence + case class Equivocation(height: Int, time: RFC3339, power: String, consensusAddress: String) implicit val equivocationReads: Reads[Equivocation] = Json.reads[Equivocation] implicit val equivocationWrites: OWrites[Equivocation] = Json.writes[Equivocation] - //evidence case class SubmitEvidence(submitter: String, evidence: Equivocation) extends TransactionMessage { def getSigners: Seq[String] = Seq(submitter) } diff --git a/app/queries/Abstract/TendermintEvidence.scala b/app/queries/Abstract/TendermintEvidence.scala index c7345b9be..506be7ac2 100644 --- a/app/queries/Abstract/TendermintEvidence.scala +++ b/app/queries/Abstract/TendermintEvidence.scala @@ -3,14 +3,10 @@ package queries.Abstract import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json.{JsObject, JsPath, Reads} import queries.responses.blockchain.BlockResponse.tendermintEvidenceApply -import utilities.MicroNumber +import utilities.Blockchain.SlashingEvidence abstract class TendermintEvidence { - val height: Int - val timeStamp: String - val validatorHexAddress: String - val validatorPower: MicroNumber - val totalVotingPower: MicroNumber + def getSlashingEvidences: Seq[SlashingEvidence] } object TendermintEvidence { diff --git a/app/queries/responses/blockchain/BlockResponse.scala b/app/queries/responses/blockchain/BlockResponse.scala index 02267b3d6..798ad148e 100644 --- a/app/queries/responses/blockchain/BlockResponse.scala +++ b/app/queries/responses/blockchain/BlockResponse.scala @@ -6,6 +6,8 @@ import play.api.libs.json.{JsObject, Json, Reads} import queries.Abstract.TendermintEvidence import queries.responses.common.Header import transactions.Abstract.BaseResponse +import utilities.Blockchain.SlashingEvidence +import utilities.Date.RFC3339 import utilities.MicroNumber object BlockResponse { @@ -14,26 +16,22 @@ object BlockResponse { private implicit val logger: Logger = Logger(this.getClass) - case class Vote(`type`: Int, height: String, round: Int, timestamp: String, validator_address: String, validator_index: Int, signature: String) + case class Vote(`type`: Int, height: String, round: Int, timestamp: RFC3339, validator_address: String, validator_index: Int, signature: String) implicit val voteReads: Reads[Vote] = Json.reads[Vote] - case class DuplicateVoteEvidence(vote_a: Vote, vote_b: Vote, TotalVotingPower: String, ValidatorPower: String, Timestamp: String) extends TendermintEvidence { - val height: Int = vote_a.height.toInt - val timeStamp: String = Timestamp - val validatorHexAddress: String = vote_a.validator_address - val validatorPower: MicroNumber = MicroNumber(ValidatorPower) - val totalVotingPower: MicroNumber = MicroNumber(TotalVotingPower) + case class DuplicateVoteEvidence(vote_a: Vote, vote_b: Vote, TotalVotingPower: String, ValidatorPower: String, Timestamp: RFC3339) extends TendermintEvidence { + def getSlashingEvidences: Seq[SlashingEvidence] = Seq(SlashingEvidence(height = vote_a.height.toInt, time = Timestamp, validatorHexAddress = vote_a.validator_address, validatorPower = MicroNumber(ValidatorPower))) } implicit val duplicateVoteEvidenceReads: Reads[DuplicateVoteEvidence] = Json.reads[DuplicateVoteEvidence] - case class LightClientAttackEvidence(CommonHeight: String, Timestamp: String) extends TendermintEvidence { - val height: Int = CommonHeight.toInt - val timeStamp: String = Timestamp - val validatorHexAddress: String = "" - val validatorPower: MicroNumber = MicroNumber.zero - val totalVotingPower: MicroNumber = MicroNumber.zero + case class ByzantineValidator(address: String, voting_power: String) + + implicit val byzantineValidatorReads: Reads[ByzantineValidator] = Json.reads[ByzantineValidator] + + case class LightClientAttackEvidence(CommonHeight: String, ByzantineValidators: Seq[ByzantineValidator], TotalVotingPower: String, Timestamp: RFC3339) extends TendermintEvidence { + def getSlashingEvidences: Seq[SlashingEvidence] = this.ByzantineValidators.map(validator => SlashingEvidence(height = this.CommonHeight.toInt, time = this.Timestamp, validatorHexAddress = validator.address, validatorPower = MicroNumber(validator.voting_power))) } implicit val lightClientAttackEvidenceReads: Reads[LightClientAttackEvidence] = Json.reads[LightClientAttackEvidence] diff --git a/app/queries/responses/blockchain/GenesisResponse.scala b/app/queries/responses/blockchain/GenesisResponse.scala index 58cd4d0ce..8f4b3e954 100644 --- a/app/queries/responses/blockchain/GenesisResponse.scala +++ b/app/queries/responses/blockchain/GenesisResponse.scala @@ -12,6 +12,7 @@ import queries.responses.blockchain.TransactionResponse._ import queries.responses.blockchain.params._ import queries.responses.common.{Coin, Delegation, Validator} import transactions.Abstract.BaseResponse +import utilities.Date.RFC3339 import utilities.MicroNumber object GenesisResponse { @@ -54,7 +55,7 @@ object GenesisResponse { object Authz { - case class Authorization(granter: String, grantee: String, expiration: String, authorization: commonAuthz.Authorization) + case class Authorization(granter: String, grantee: String, expiration: RFC3339, authorization: commonAuthz.Authorization) implicit val authorizationReads: Reads[Authorization] = Json.reads[Authorization] @@ -130,7 +131,7 @@ object GenesisResponse { object Staking { - case class RedelegationEntry(creation_height: String, completion_time: String, initial_balance: String, shares_dst: String) { + case class RedelegationEntry(creation_height: String, completion_time: RFC3339, initial_balance: String, shares_dst: String) { def toRedelegationEntry: Serializable.RedelegationEntry = Serializable.RedelegationEntry(creationHeight = creation_height.toInt, completionTime = completion_time, initialBalance = MicroNumber(BigInt(initial_balance)), sharesDestination = BigDecimal(shares_dst)) } @@ -142,7 +143,7 @@ object GenesisResponse { implicit val redelegationReads: Reads[Redelegation] = Json.reads[Redelegation] - case class UndelegationEntry(creation_height: String, completion_time: String, initial_balance: String, balance: String) { + case class UndelegationEntry(creation_height: String, completion_time: RFC3339, initial_balance: String, balance: String) { def toUndelegationEntry: Serializable.UndelegationEntry = Serializable.UndelegationEntry(creationHeight = creation_height.toInt, completionTime = completion_time, initialBalance = MicroNumber(BigInt(initial_balance)), balance = MicroNumber(BigInt(balance))) } @@ -185,7 +186,7 @@ object GenesisResponse { implicit val appStateReads: Reads[AppState] = Json.reads[AppState] - case class Genesis(app_state: AppState, genesis_time: String, chain_id: String, initial_height: String) + case class Genesis(app_state: AppState, genesis_time: RFC3339, chain_id: String, initial_height: String) implicit val genesisReads: Reads[Genesis] = Json.reads[Genesis] diff --git a/app/queries/responses/blockchain/ProposalResponse.scala b/app/queries/responses/blockchain/ProposalResponse.scala index cd5c56972..a91ee7a95 100644 --- a/app/queries/responses/blockchain/ProposalResponse.scala +++ b/app/queries/responses/blockchain/ProposalResponse.scala @@ -1,23 +1,22 @@ package queries.responses.blockchain import models.blockchain.{Proposal => BlockchainProposal} +import models.common.Serializable import play.api.libs.json.{Json, Reads} import queries.Abstract.ProposalContent import queries.responses.common.Coin -import models.common.Serializable import transactions.Abstract.BaseResponse +import utilities.Date.RFC3339 object ProposalResponse { - import queries.responses.common.ProposalContents._ - case class FinalTallyResult(yes: String, abstain: String, no: String, no_with_veto: String) { def toSerializableFinalTallyResult: Serializable.FinalTallyResult = Serializable.FinalTallyResult(yes = BigDecimal(yes), abstain = BigDecimal(abstain), no = BigDecimal(no), noWithVeto = BigDecimal(no_with_veto)) } implicit val finalTallyResultReads: Reads[FinalTallyResult] = Json.reads[FinalTallyResult] - case class Proposal(proposal_id: String, content: ProposalContent, status: String, final_tally_result: FinalTallyResult, submit_time: String, deposit_end_time: String, total_deposit: Seq[Coin], voting_start_time: String, voting_end_time: String) { + case class Proposal(proposal_id: String, content: ProposalContent, status: String, final_tally_result: FinalTallyResult, submit_time: RFC3339, deposit_end_time: RFC3339, total_deposit: Seq[Coin], voting_start_time: RFC3339, voting_end_time: RFC3339) { def toSerializableProposal: BlockchainProposal = BlockchainProposal(id = proposal_id.toInt, content = content.toSerializableProposalContent, status = status, finalTallyResult = final_tally_result.toSerializableFinalTallyResult, submitTime = submit_time, depositEndTime = deposit_end_time, totalDeposit = total_deposit.map(_.toCoin), votingStartTime = voting_start_time, votingEndTime = voting_end_time) } diff --git a/app/queries/responses/blockchain/TransactionResponse.scala b/app/queries/responses/blockchain/TransactionResponse.scala index ff6bb4126..d02eabf1c 100644 --- a/app/queries/responses/blockchain/TransactionResponse.scala +++ b/app/queries/responses/blockchain/TransactionResponse.scala @@ -10,6 +10,7 @@ import queries.responses.common.Coin import queries.responses.common.PublicKeys._ import queries.responses.common.TransactionMessageResponses.msgApply import transactions.Abstract.BaseResponse +import utilities.Date.RFC3339 object TransactionResponse { @@ -44,7 +45,7 @@ object TransactionResponse { implicit val txReads: Reads[Tx] = Json.reads[Tx] - case class TxResponse(height: String, txhash: String, code: Int, raw_log: String, gas_wanted: String, gas_used: String, tx: Tx, timestamp: String) { + case class TxResponse(height: String, txhash: String, code: Int, raw_log: String, gas_wanted: String, gas_used: String, tx: Tx, timestamp: RFC3339) { def toTransaction: Transaction = Transaction( hash = txhash, height = height.toInt, diff --git a/app/queries/responses/common/FeeGrant.scala b/app/queries/responses/common/FeeGrant.scala index 0892fad34..8d4135612 100644 --- a/app/queries/responses/common/FeeGrant.scala +++ b/app/queries/responses/common/FeeGrant.scala @@ -6,6 +6,7 @@ import play.api.Logger import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json.{JsObject, JsPath, Json, Reads} import queries.Abstract.FeeGrant.FeeAllowance +import utilities.Date.RFC3339 object FeeGrant { @@ -22,13 +23,13 @@ object FeeGrant { JsPath.read[JsObject] ) (allowanceApply _) - case class BasicAllowance(spend_limit: Seq[Coin], expiration: Option[String]) extends FeeAllowance { + case class BasicAllowance(spend_limit: Seq[Coin], expiration: Option[RFC3339]) extends FeeAllowance { def toSerializable: commonFeeGrant.BasicAllowance = commonFeeGrant.BasicAllowance(spendLimit = spend_limit.map(_.toCoin), expiration = expiration) } implicit val basicAllowanceReads: Reads[BasicAllowance] = Json.reads[BasicAllowance] - case class PeriodicAllowance(basic: BasicAllowance, period: String, period_spend_limit: Seq[Coin], period_can_spend: Seq[Coin], period_reset: String) extends FeeAllowance { + case class PeriodicAllowance(basic: BasicAllowance, period: String, period_spend_limit: Seq[Coin], period_can_spend: Seq[Coin], period_reset: RFC3339) extends FeeAllowance { def toSerializable: commonFeeGrant.PeriodicAllowance = commonFeeGrant.PeriodicAllowance(basicAllowance = basic.toSerializable, period = period, periodSpendLimit = period_spend_limit.map(_.toCoin), periodCanSpend = period_can_spend.map(_.toCoin), periodReset = period_reset) } diff --git a/app/queries/responses/common/Header.scala b/app/queries/responses/common/Header.scala index a740641cd..193987d4f 100644 --- a/app/queries/responses/common/Header.scala +++ b/app/queries/responses/common/Header.scala @@ -2,12 +2,13 @@ package queries.responses.common import play.api.libs.functional.syntax._ import play.api.libs.json.{JsPath, Reads} +import utilities.Date.RFC3339 -case class Header(chain_id: String, height: Int, time: String, data_hash: String, evidence_hash: String, validators_hash: String, proposer_address: String) +case class Header(chain_id: String, height: Int, time: RFC3339, data_hash: String, evidence_hash: String, validators_hash: String, proposer_address: String) object Header { - def apply2(chain_id: String, height: String, time: String, data_hash: String, evidence_hash: String, validators_hash: String, proposer_address: String): Header = new Header(chain_id, height.toInt, time, data_hash, evidence_hash, validators_hash, proposer_address) + def apply2(chain_id: String, height: String, time: String, data_hash: String, evidence_hash: String, validators_hash: String, proposer_address: String): Header = new Header(chain_id, height.toInt, RFC3339(time), data_hash, evidence_hash, validators_hash, proposer_address) implicit val headerReads: Reads[Header] = ( (JsPath \ "chain_id").read[String] and diff --git a/app/queries/responses/common/ProposalContents.scala b/app/queries/responses/common/ProposalContents.scala index a524829cf..7c20e4cdc 100644 --- a/app/queries/responses/common/ProposalContents.scala +++ b/app/queries/responses/common/ProposalContents.scala @@ -6,13 +6,14 @@ import play.api.Logger import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json.{JsObject, JsPath, Json, OWrites, Reads} import queries.Abstract.ProposalContent +import utilities.Date.RFC3339 object ProposalContents { private implicit val module: String = constants.Module.PROPOSAL_CONTENT_RESPONSE private implicit val logger: Logger = Logger(this.getClass) - case class Plan(name: String, time: String, height: String, info: String) { + case class Plan(name: String, time: RFC3339, height: String, info: String) { def toSerializablePlan: SerializableProposalContents.Plan = SerializableProposalContents.Plan(name = name, time = time, height = height, info = info) } diff --git a/app/queries/responses/common/Redelegation.scala b/app/queries/responses/common/Redelegation.scala index 54f81fd37..46fc24a5e 100644 --- a/app/queries/responses/common/Redelegation.scala +++ b/app/queries/responses/common/Redelegation.scala @@ -4,6 +4,7 @@ import models.blockchain.{Redelegation => BlockchainRedelegation} import models.common.Serializable import play.api.libs.functional.syntax._ import play.api.libs.json.{JsPath, Json, Reads} +import utilities.Date.RFC3339 import utilities.MicroNumber case class Redelegation(delegator_address: String, validator_src_address: String, validator_dst_address: String) @@ -12,15 +13,15 @@ object Redelegation { implicit val redelegationReads: Reads[Redelegation] = Json.reads[Redelegation] - case class RedelegationEntry(creation_height: Int, completion_time: String, initial_balance: MicroNumber, shares_dst: String) { - def toRedelegationEntry: Serializable.RedelegationEntry = Serializable.RedelegationEntry(creationHeight = creation_height.toInt, completionTime = completion_time, initialBalance = initial_balance, sharesDestination = BigDecimal(shares_dst)) + case class RedelegationEntry(creation_height: Int, completion_time: RFC3339, initial_balance: MicroNumber, shares_dst: String) { + def toRedelegationEntry: Serializable.RedelegationEntry = Serializable.RedelegationEntry(creationHeight = creation_height, completionTime = completion_time, initialBalance = initial_balance, sharesDestination = BigDecimal(shares_dst)) } - def redelegationEntryApply(creation_height: Int, completion_time: String, initial_balance: String, shares_dst: String): RedelegationEntry = RedelegationEntry(creation_height = creation_height, completion_time = completion_time, initial_balance = new MicroNumber(BigDecimal(initial_balance).toBigInt), shares_dst = shares_dst) + def redelegationEntryApply(creation_height: Int, completion_time: RFC3339, initial_balance: String, shares_dst: String): RedelegationEntry = RedelegationEntry(creation_height = creation_height, completion_time = completion_time, initial_balance = new MicroNumber(BigDecimal(initial_balance).toBigInt), shares_dst = shares_dst) implicit val redelegationEntryReads: Reads[RedelegationEntry] = ( (JsPath \ "creation_height").read[Int] and - (JsPath \ "completion_time").read[String] and + (JsPath \ "completion_time").read[RFC3339] and (JsPath \ "initial_balance").read[String] and (JsPath \ "shares_dst").read[String] ) (redelegationEntryApply _) diff --git a/app/queries/responses/common/SigningInfo.scala b/app/queries/responses/common/SigningInfo.scala index 4f839c0a2..87fcdb115 100644 --- a/app/queries/responses/common/SigningInfo.scala +++ b/app/queries/responses/common/SigningInfo.scala @@ -1,10 +1,11 @@ package queries.responses.common import play.api.libs.json.{Json, Reads} +import utilities.Date.RFC3339 object SigningInfo { - case class Result(address: String, start_height: String, index_offset: String, jailed_until: String, tombstoned: Boolean, missed_blocks_counter: String) + case class Result(address: String, start_height: String, index_offset: String, jailed_until: RFC3339, tombstoned: Boolean, missed_blocks_counter: String) implicit val resultReads: Reads[Result] = Json.reads[Result] diff --git a/app/queries/responses/common/TransactionMessageResponses.scala b/app/queries/responses/common/TransactionMessageResponses.scala index ecfaaee89..a925b4869 100644 --- a/app/queries/responses/common/TransactionMessageResponses.scala +++ b/app/queries/responses/common/TransactionMessageResponses.scala @@ -7,6 +7,7 @@ import play.api.Logger import play.api.libs.json.{JsObject, Json, Reads} import queries.Abstract.{ProposalContent, PublicKey, TransactionMessageResponse} import queries.responses.blockchain.TransactionResponse.Msg +import utilities.Date.RFC3339 import utilities.MicroNumber object TransactionMessageResponses { @@ -25,7 +26,7 @@ object TransactionMessageResponses { implicit val createVestingAccountReads: Reads[CreateVestingAccount] = Json.reads[CreateVestingAccount] //authz - case class Grant(authorization: Authz.Authorization, expiration: String) { + case class Grant(authorization: Authz.Authorization, expiration: RFC3339) { def toSerializable: TransactionMessages.Grant = TransactionMessages.Grant(authorization = authorization.toSerializable, expiration = expiration) } @@ -106,8 +107,8 @@ object TransactionMessageResponses { implicit val fundCommunityPoolReads: Reads[FundCommunityPool] = Json.reads[FundCommunityPool] - //evidence - TODO As evidence interface - case class Equivocation(height: String, time: String, power: String, consensus_address: String) { + //evidence - TODO evidence as interface - there is only struct which implements this (Equivocation) as of v0.44.5 + case class Equivocation(height: String, time: RFC3339, power: String, consensus_address: String) { def toEvidence: TransactionMessages.Equivocation = TransactionMessages.Equivocation(height = height.toInt, time = time, power = power, consensusAddress = consensus_address) } @@ -120,7 +121,6 @@ object TransactionMessageResponses { implicit val submitEvidenceReads: Reads[SubmitEvidence] = Json.reads[SubmitEvidence] // feeGrant - case class FeeGrantAllowance(granter: String, grantee: String, allowance: FeeGrant.Allowance) extends TransactionMessageResponse { def toTxMsg: TransactionMessage = TransactionMessages.FeeGrantAllowance(granter = granter, grantee = grantee, allowance = allowance.toSerializable) } @@ -172,7 +172,7 @@ object TransactionMessageResponses { implicit val commissionRatesReads: Reads[CommissionRates] = Json.reads[CommissionRates] - case class Commission(commission_rates: CommissionRates, update_time: String) { + case class Commission(commission_rates: CommissionRates, update_time: RFC3339) { def toCommission: Serializable.Validator.Commission = Serializable.Validator.Commission(commissionRates = commission_rates.toCommissionRates, updateTime = update_time) } diff --git a/app/queries/responses/common/Undelegation.scala b/app/queries/responses/common/Undelegation.scala index b2facc45e..52792aea5 100644 --- a/app/queries/responses/common/Undelegation.scala +++ b/app/queries/responses/common/Undelegation.scala @@ -4,19 +4,20 @@ import models.blockchain.{Undelegation => BlockchainUndelegation} import models.common.Serializable import play.api.libs.functional.syntax._ import play.api.libs.json.{JsPath, Json, Reads} +import utilities.Date.RFC3339 import utilities.MicroNumber object Undelegation { - case class Entry(creation_height: String, completion_time: String, initial_balance: MicroNumber, balance: MicroNumber) { + case class Entry(creation_height: String, completion_time: RFC3339, initial_balance: MicroNumber, balance: MicroNumber) { def toUndelegationEntry: Serializable.UndelegationEntry = Serializable.UndelegationEntry(creationHeight = creation_height.toInt, completionTime = completion_time, initialBalance = initial_balance, balance = balance) } - def entryApply(creation_height: String, completion_time: String, initial_balance: String, balance: String): Entry = Entry(creation_height, completion_time, new MicroNumber(BigInt(initial_balance)), new MicroNumber(BigInt(balance))) + def entryApply(creation_height: String, completion_time: RFC3339, initial_balance: String, balance: String): Entry = Entry(creation_height, completion_time, new MicroNumber(BigInt(initial_balance)), new MicroNumber(BigInt(balance))) implicit val entryReads: Reads[Entry] = ( (JsPath \ "creation_height").read[String] and - (JsPath \ "completion_time").read[String] and + (JsPath \ "completion_time").read[RFC3339] and (JsPath \ "initial_balance").read[String] and (JsPath \ "balance").read[String] ) (entryApply _) diff --git a/app/queries/responses/common/Validator.scala b/app/queries/responses/common/Validator.scala index b6d4a36e2..7f1b3a1eb 100644 --- a/app/queries/responses/common/Validator.scala +++ b/app/queries/responses/common/Validator.scala @@ -5,6 +5,7 @@ import models.common.Serializable import play.api.libs.functional.syntax.toFunctionalBuilderOps import play.api.libs.json.{JsPath, Json, Reads} import queries.Abstract.PublicKey +import utilities.Date.RFC3339 import utilities.MicroNumber object Validator { @@ -15,7 +16,7 @@ object Validator { implicit val commissionRatesReads: Reads[CommissionRates] = Json.reads[CommissionRates] - case class Commission(commission_rates: CommissionRates, update_time: String) { + case class Commission(commission_rates: CommissionRates, update_time: RFC3339) { def toCommission: Serializable.Validator.Commission = Serializable.Validator.Commission(commissionRates = commission_rates.toCommissionRates, updateTime = update_time) } @@ -27,7 +28,7 @@ object Validator { implicit val descriptionReads: Reads[Description] = Json.reads[Description] - case class Result(operator_address: String, consensus_pubkey: PublicKey, jailed: Boolean, status: String, tokens: MicroNumber, delegator_shares: BigDecimal, description: Description, unbonding_height: String, unbonding_time: String, commission: Commission, min_self_delegation: String) { + case class Result(operator_address: String, consensus_pubkey: PublicKey, jailed: Boolean, status: String, tokens: MicroNumber, delegator_shares: BigDecimal, description: Description, unbonding_height: String, unbonding_time: RFC3339, commission: Commission, min_self_delegation: String) { def toValidator: BlockchainValidator = BlockchainValidator( operatorAddress = operator_address, hexAddress = utilities.Bech32.convertValidatorPublicKeyToHexAddress(consensus_pubkey.toSerializablePublicKey.value), @@ -43,7 +44,7 @@ object Validator { minimumSelfDelegation = MicroNumber(min_self_delegation)) } - def resultApply(operator_address: String, consensus_pubkey: PublicKey, jailed: Boolean, status: String, tokens: String, delegator_shares: BigDecimal, description: Description, unbonding_height: String, unbonding_time: String, commission: Commission, min_self_delegation: String): Result = Result( + def resultApply(operator_address: String, consensus_pubkey: PublicKey, jailed: Boolean, status: String, tokens: String, delegator_shares: BigDecimal, description: Description, unbonding_height: String, unbonding_time: RFC3339, commission: Commission, min_self_delegation: String): Result = Result( operator_address = operator_address, consensus_pubkey = consensus_pubkey, jailed = jailed, status = status, tokens = new MicroNumber(BigInt(tokens)), delegator_shares = delegator_shares, description = description, unbonding_height = unbonding_height, unbonding_time = unbonding_time, commission = commission, min_self_delegation = min_self_delegation) implicit val resultReads: Reads[Result] = ( @@ -55,7 +56,7 @@ object Validator { (JsPath \ "delegator_shares").read[BigDecimal] and (JsPath \ "description").read[Description] and (JsPath \ "unbonding_height").read[String] and - (JsPath \ "unbonding_time").read[String] and + (JsPath \ "unbonding_time").read[RFC3339] and (JsPath \ "commission").read[Commission] and (JsPath \ "min_self_delegation").read[String] ) (resultApply _) diff --git a/app/services/Block.scala b/app/services/Block.scala index 466fc6aad..2820f1664 100644 --- a/app/services/Block.scala +++ b/app/services/Block.scala @@ -15,6 +15,7 @@ import queries.responses.blockchain.BlockResponse.{Response => BlockResponse} import queries.responses.blockchain.TransactionByHeightResponse.{Response => TransactionByHeightResponse} import queries.responses.common.ProposalContents.CommunityPoolSpend import queries.responses.common.{Event, Header} +import utilities.Date.RFC3339 import utilities.MicroNumber import javax.inject.{Inject, Singleton} @@ -149,7 +150,7 @@ class Block @Inject()( def getWebSocketNewBlock(proposer: String): actorsMessage.WebSocket.NewBlock = actorsMessage.WebSocket.NewBlock( block = actorsMessage.WebSocket.Block( height = blockCommitResponse.result.signed_header.header.height, - time = blockCommitResponse.result.signed_header.header.time, + time = blockCommitResponse.result.signed_header.header.time.toString, proposer = proposer), txs = transactions.map(tx => actorsMessage.WebSocket.Tx( hash = tx.hash, @@ -179,11 +180,13 @@ class Block @Inject()( val deductFeesFrom = if (transaction.getFeeGranter != "") { val grantee = transaction.getFeePayer val feeGrant = blockchainFeeGrants.Service.tryGet(granter = transaction.getFeeGranter, grantee = grantee) + def updateOrDeleteFeeGrant(feeGrant: FeeGrant) = { val response = feeGrant.allowance.validate(blockTime = header.time, fees = transaction.fee.amount) if (response.delete) blockchainFeeGrants.Service.delete(granter = transaction.getFeeGranter, grantee = grantee) else blockchainFeeGrants.Service.insertOrUpdate(feeGrant.copy(allowance = response.updated)) } + for { feeGrant <- feeGrant _ <- updateOrDeleteFeeGrant(feeGrant) @@ -326,8 +329,8 @@ class Block @Inject()( val operatorAddress = if (hexAddress != "") blockchainValidators.Service.tryGetOperatorAddress(hexAddress) else Future(throw new BaseException(constants.Response.SLASHING_EVENT_ADDRESS_NOT_FOUND)) // Shouldn't throw exception because even with light client attack reason double signing and validator address is present - def getDistributionHeight(slashingReason: String) = if (slashingReason == constants.Blockchain.Event.Attribute.MissingSignature) Future(height - 2) - else Future(blockResponse.result.block.evidence.evidence.find(_.validatorHexAddress == hexAddress).getOrElse(throw new BaseException(constants.Response.TENDERMINT_EVIDENCE_NOT_FOUND)).height - 1) + def getDistributionHeight(slashingReason: String) = if (slashingReason == constants.Blockchain.Event.Attribute.MissingSignature) (height - 2) + else (blockResponse.result.block.evidence.evidence.flatMap(_.getSlashingEvidences).find(_.validatorHexAddress == hexAddress).getOrElse(throw new BaseException(constants.Response.TENDERMINT_EVIDENCE_NOT_FOUND)).height - 1) def slashing(operatorAddress: String, slashingReason: String, distributionHeight: Int) = { val slashingFraction = if (slashingReason == constants.Blockchain.Event.Attribute.MissingSignature) slashingParameter.slashFractionDowntime else slashingParameter.slashFractionDoubleSign @@ -338,8 +341,7 @@ class Block @Inject()( for { operatorAddress <- operatorAddress slashingReason <- slashingReason - distributionHeight <- getDistributionHeight(slashingReason) - _ <- slashing(operatorAddress = operatorAddress, slashingReason = slashingReason, distributionHeight = distributionHeight) + _ <- slashing(operatorAddress = operatorAddress, slashingReason = slashingReason, distributionHeight = getDistributionHeight(slashingReason)) } yield (operatorAddress, slashingReason) }) } @@ -470,7 +472,7 @@ class Block @Inject()( } }) - def onUnbondingCompletionEvents(unbondingCompletionEvents: Seq[Event], currentBlockTimeStamp: String): Future[Seq[Unit]] = utilitiesOperations.traverse(unbondingCompletionEvents)(event => { + def onUnbondingCompletionEvents(unbondingCompletionEvents: Seq[Event], currentBlockTimeStamp: RFC3339): Future[Seq[Unit]] = utilitiesOperations.traverse(unbondingCompletionEvents)(event => { val validator = event.attributes.find(_.key == constants.Blockchain.Event.Attribute.Validator).fold("")(_.value.getOrElse("")) val delegator = event.attributes.find(_.key == constants.Blockchain.Event.Attribute.Delegator).fold("")(_.value.getOrElse("")) val process = if (validator != "" && delegator != "") blockchainUndelegations.Utility.onUnbondingCompletionEvent(delegatorAddress = delegator, validatorAddress = validator, currentBlockTimeStamp = currentBlockTimeStamp) else Future(throw new BaseException(constants.Response.INVALID_UNBONDING_COMPLETION_EVENT)) @@ -482,7 +484,7 @@ class Block @Inject()( } }) - def onRedelegationCompletionEvents(redelegationCompletionEvents: Seq[Event], currentBlockTimeStamp: String): Future[Seq[Unit]] = utilitiesOperations.traverse(redelegationCompletionEvents)(event => { + def onRedelegationCompletionEvents(redelegationCompletionEvents: Seq[Event], currentBlockTimeStamp: RFC3339): Future[Seq[Unit]] = utilitiesOperations.traverse(redelegationCompletionEvents)(event => { val srcValidator = event.attributes.find(_.key == constants.Blockchain.Event.Attribute.SrcValidator).fold("")(_.value.getOrElse("")) val dstValidator = event.attributes.find(_.key == constants.Blockchain.Event.Attribute.DstValidator).fold("")(_.value.getOrElse("")) val delegator = event.attributes.find(_.key == constants.Blockchain.Event.Attribute.Delegator).fold("")(_.value.getOrElse("")) @@ -495,7 +497,7 @@ class Block @Inject()( } }) - private def slash(validatorAddress: String, infractionHeight: Int, currentBlockHeight: Int, currentBlockTIme: String, slashingFraction: BigDecimal) = { + private def slash(validatorAddress: String, infractionHeight: Int, currentBlockHeight: Int, currentBlockTIme: RFC3339, slashingFraction: BigDecimal) = { val validator = blockchainValidators.Service.tryGet(validatorAddress) def update(validator: Validator) = if (!validator.isUnbonded && infractionHeight < currentBlockHeight) { diff --git a/app/services/Startup.scala b/app/services/Startup.scala index d764e18e8..32ae6e0d0 100644 --- a/app/services/Startup.scala +++ b/app/services/Startup.scala @@ -19,6 +19,7 @@ import queries.responses.blockchain.MintingInflationResponse.{Response => Mintin import queries.responses.blockchain.StakingPoolResponse.{Response => StakingPoolResponse} import queries.responses.blockchain.TotalSupplyResponse.{Response => TotalSupplyResponse} import queries.responses.common.Header +import utilities.Date.RFC3339 import utilities.MicroNumber import javax.inject.{Inject, Singleton} @@ -147,7 +148,7 @@ class Startup @Inject()( } // IMPORTANT: Assuming all GenTxs are valid txs and successfully goes through - private def insertGenesisTransactionsOnStart(genTxs: Seq[GenTx], chainID: String, initialHeight: Int, genesisTime: String): Future[Unit] = { + private def insertGenesisTransactionsOnStart(genTxs: Seq[GenTx], chainID: String, initialHeight: Int, genesisTime: RFC3339): Future[Unit] = { val updateTxs = utilitiesOperations.traverse(genTxs) { genTx => val updateTx = utilitiesOperations.traverse(genTx.body.messages)(txMsg => blocksServices.actionOnTxMessages(txMsg.toStdMsg)(Header(chain_id = chainID, height = initialHeight, time = genesisTime, data_hash = "", evidence_hash = "", validators_hash = "", proposer_address = ""))) val updateAccount = utilitiesOperations.traverse(genTx.getSigners)(signer => blockchainAccounts.Utility.incrementSequence(signer)) @@ -252,7 +253,7 @@ class Startup @Inject()( } } - private def actionsOnEvents(blockResultResponse: BlockResultResponse, currentBlockTimeStamp: String): Future[Unit] = { + private def actionsOnEvents(blockResultResponse: BlockResultResponse, currentBlockTimeStamp: RFC3339): Future[Unit] = { val slashing = blocksServices.onSlashingEvents(blockResultResponse.result.begin_block_events.filter(_.`type` == constants.Blockchain.Event.Slash).map(_.decode), blockResultResponse.result.height.toInt) val missedBlock = blocksServices.onMissedBlockEvents(blockResultResponse.result.begin_block_events.filter(_.`type` == constants.Blockchain.Event.Liveness).map(_.decode), blockResultResponse.result.height.toInt) val unbondingCompletion = blocksServices.onUnbondingCompletionEvents(unbondingCompletionEvents = blockResultResponse.result.end_block_events.getOrElse(Seq()).filter(_.`type` == constants.Blockchain.Event.CompleteUnbonding).map(_.decode), currentBlockTimeStamp = currentBlockTimeStamp) diff --git a/app/utilities/Blockchain.scala b/app/utilities/Blockchain.scala index 0aa9ca65b..d379e2d45 100644 --- a/app/utilities/Blockchain.scala +++ b/app/utilities/Blockchain.scala @@ -3,6 +3,7 @@ package utilities import models.Abstract.Authz.Authorization import models.common.FeeGrant.Allowance import models.common.Serializable.Coin +import utilities.Date.RFC3339 object Blockchain { @@ -23,4 +24,6 @@ object Blockchain { object FeeGrant { case class ValidateResponse(delete: Boolean, updated: Allowance) } + + case class SlashingEvidence(height: Int, time: RFC3339, validatorHexAddress: String, validatorPower: MicroNumber) } diff --git a/app/utilities/Date.scala b/app/utilities/Date.scala index a76bc12fa..bc3f05dc7 100644 --- a/app/utilities/Date.scala +++ b/app/utilities/Date.scala @@ -2,10 +2,11 @@ package utilities import exceptions.BaseException import play.api.Logger +import play.api.libs.json._ import java.sql.Timestamp +import java.time._ import java.time.format.{DateTimeFormatter, FormatStyle} -import java.time.{Instant, LocalDateTime, ZoneId, ZonedDateTime} object Date { @@ -27,63 +28,89 @@ object Date { case _: Exception => new Timestamp(System.currentTimeMillis()) } - def bcTimestampToZonedDateTime(timestamp: String): ZonedDateTime = try { - ZonedDateTime.parse(timestamp) - } catch { - case exception: Exception => logger.error(exception.getLocalizedMessage) - ZonedDateTime.now() - } - - def bcTimestampToString(timestamp: String): String = try { - ZonedDateTime.parse(timestamp).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)).format(dateTimeFormat) - } catch { - case exception: Exception => logger.error(exception.getLocalizedMessage) - timestamp + def getEpoch(time: String): Long = { + time.split("").last match { + case "s" => time.dropRight(1).toLong + case "m" => 60 * time.dropRight(1).toLong + case "h" => 60 * 60 * time.dropRight(1).toLong + case _ => throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + } } - def isMature(completionTimestamp: String, currentTimeStamp: String): Boolean = try { - val completionTime = ZonedDateTime.parse(completionTimestamp) - val currentTime = ZonedDateTime.parse(currentTimeStamp) - currentTime.isEqual(completionTime) || currentTime.isAfter(completionTime) + def isValidRFC3339(x: String): Boolean = try { + ZonedDateTime.parse(x) + true } catch { case exception: Exception => logger.error(exception.getMessage) throw new BaseException(constants.Response.DATE_FORMAT_ERROR) } - def isAfter(t1: String, t2: String): Boolean = try { - ZonedDateTime.parse(t1).isAfter(ZonedDateTime.parse(t2)) - } catch { - case exception: Exception => logger.error(exception.getMessage) - throw new BaseException(constants.Response.DATE_FORMAT_ERROR) - } + class RFC3339(timestamp: String) { - def isBefore(t1: String, t2: String): Boolean = try { - ZonedDateTime.parse(t1).isBefore(ZonedDateTime.parse(t2)) - } catch { - case exception: Exception => logger.error(exception.getMessage) - throw new BaseException(constants.Response.DATE_FORMAT_ERROR) - } + def zonedDateTime: ZonedDateTime = ZonedDateTime.parse(this.timestamp) - def addTime(timestamp: String, addEpochTime: Long): String = try { - ZonedDateTime.ofInstant(Instant.ofEpochSecond(ZonedDateTime.parse(timestamp).toEpochSecond + addEpochTime), ZoneId.of("UTC")).format(DateTimeFormatter.ISO_ZONED_DATE_TIME) - } catch { - case exception: Exception => logger.error(exception.getLocalizedMessage) - throw new BaseException(constants.Response.INVALID_DATA_TYPE) - } + def unix: Long = zonedDateTime.toEpochSecond - def addTime(t1: String, t2: String): String = try { - ZonedDateTime.ofInstant(Instant.ofEpochSecond(ZonedDateTime.parse(t1).toEpochSecond + ZonedDateTime.parse(t2).toEpochSecond), ZoneId.of("UTC")).format(DateTimeFormatter.ISO_ZONED_DATE_TIME) - } catch { - case exception: Exception => logger.error(exception.getMessage) - throw new BaseException(constants.Response.DATE_FORMAT_ERROR) - } + override def toString: String = this.timestamp - def getEpoch(time: String): Long = { - time.split("").last match { - case "s" => time.dropRight(1).toLong - case "m" => 60 * time.dropRight(1).toLong - case "h" => 60 * 60 * time.dropRight(1).toLong - case _ => throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + def isAfter(that: RFC3339): Boolean = try { + this.zonedDateTime.isAfter(that.zonedDateTime) + } catch { + case exception: Exception => logger.error(exception.getMessage) + throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + } + + def isBefore(that: RFC3339): Boolean = try { + this.zonedDateTime.isBefore(that.zonedDateTime) + } catch { + case exception: Exception => logger.error(exception.getMessage) + throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + } + + def isEqual(that: RFC3339): Boolean = this.zonedDateTime.isEqual(that.zonedDateTime) + + def isAfterOrEqual(that: RFC3339): Boolean = try { + val thisTime = this.zonedDateTime + val thatTime = that.zonedDateTime + thisTime.isEqual(thatTime) || thisTime.isAfter(thatTime) + } catch { + case exception: Exception => logger.error(exception.getMessage) + throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + } + + def isBeforeOrEqual(that: RFC3339): Boolean = try { + val thisTime = this.zonedDateTime + val thatTime = that.zonedDateTime + thisTime.isEqual(thatTime) || thisTime.isBefore(thatTime) + } catch { + case exception: Exception => logger.error(exception.getMessage) + throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + } + + def addEpoch(epoch: Long): RFC3339 = try { + RFC3339(ZonedDateTime.ofInstant(Instant.ofEpochSecond(this.unix + epoch), ZoneId.of("UTC")).format(DateTimeFormatter.ISO_ZONED_DATE_TIME)) + } catch { + case exception: Exception => logger.error(exception.getLocalizedMessage) + throw new BaseException(constants.Response.INVALID_DATA_TYPE) } + + def add(that: RFC3339): RFC3339 = try { + RFC3339(ZonedDateTime.ofInstant(Instant.ofEpochSecond(this.unix + that.unix), ZoneId.of("UTC")).format(DateTimeFormatter.ISO_ZONED_DATE_TIME)) + } catch { + case exception: Exception => logger.error(exception.getMessage) + throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + } + + def difference(that: RFC3339): Duration = Duration.between(this.zonedDateTime, that.zonedDateTime) + + } + + object RFC3339 { + + def apply(value: String): RFC3339 = if (isValidRFC3339(value)) new RFC3339(value) else throw new BaseException(constants.Response.DATE_FORMAT_ERROR) + + implicit val rfc3339Writes: Writes[RFC3339] = (rfc3339: RFC3339) => Json.toJson(rfc3339.toString) + + implicit val rfc3339Reads: Reads[RFC3339] = (json: JsValue) => json.validate[String].map(x => RFC3339(x)) } } diff --git a/app/views/base/commonRFC3339Info.scala.html b/app/views/base/commonRFC3339Info.scala.html new file mode 100644 index 000000000..aea027bc0 --- /dev/null +++ b/app/views/base/commonRFC3339Info.scala.html @@ -0,0 +1,16 @@ +@import utilities.Date.RFC3339 + +@(key: String, value: RFC3339, id: String, arguments: (Symbol, Any)*)(implicit messagesProvider: MessagesProvider) + +
+
+ @Messages(key) +
+
+ @value.toString +
+
+ + \ No newline at end of file diff --git a/app/views/component/blockchain/account/accountDelegationsRedelegating.scala.html b/app/views/component/blockchain/account/accountDelegationsRedelegating.scala.html index 8b6da3911..47cceceaf 100644 --- a/app/views/component/blockchain/account/accountDelegationsRedelegating.scala.html +++ b/app/views/component/blockchain/account/accountDelegationsRedelegating.scala.html @@ -26,7 +26,7 @@ @utilities.NumericOperation.formatNumber(utilities.NumericOperation.roundOff(redelegationEntry.initialBalance.toDouble)) } diff --git a/app/views/component/blockchain/account/accountDelegationsUndelegating.scala.html b/app/views/component/blockchain/account/accountDelegationsUndelegating.scala.html index 601867647..27e7f54d5 100644 --- a/app/views/component/blockchain/account/accountDelegationsUndelegating.scala.html +++ b/app/views/component/blockchain/account/accountDelegationsUndelegating.scala.html @@ -22,7 +22,7 @@ @utilities.NumericOperation.formatNumber(utilities.NumericOperation.roundOff(undelegationEntry.balance.toDouble)) } diff --git a/app/views/component/blockchain/account/accountTransactionsPerPage.scala.html b/app/views/component/blockchain/account/accountTransactionsPerPage.scala.html index a26f8229f..83657ec8c 100644 --- a/app/views/component/blockchain/account/accountTransactionsPerPage.scala.html +++ b/app/views/component/blockchain/account/accountTransactionsPerPage.scala.html @@ -38,7 +38,7 @@ diff --git a/app/views/component/blockchain/block/blockDetails.scala.html b/app/views/component/blockchain/block/blockDetails.scala.html index a768a9868..cdc841832 100644 --- a/app/views/component/blockchain/block/blockDetails.scala.html +++ b/app/views/component/blockchain/block/blockDetails.scala.html @@ -6,11 +6,7 @@ @commonCard(constants.View.BLOCK_HEADER) { } { @commonIntInfo(constants.View.HEIGHT, block.height) - @commonTextInfo(constants.View.TIME, "", 'id -> "blockDetailsTime") + @commonRFC3339Info(constants.View.TIME, block.time, "blockDetailsTime") @commonHtmlInfo(constants.View.PROPOSER, commonJavascriptLink(proposer, JavaScript(s"componentResource('explorerContent', jsRoutes.controllers.ComponentViewController.validator('${block.proposerAddress}'))"))) @commonIntInfo(constants.View.NUMBER_OF_TRANSACTIONS, numTxs) -} - - \ No newline at end of file +} \ No newline at end of file diff --git a/app/views/component/blockchain/block/blockListPage.scala.html b/app/views/component/blockchain/block/blockListPage.scala.html index 88cb8be80..3dccb84f0 100644 --- a/app/views/component/blockchain/block/blockListPage.scala.html +++ b/app/views/component/blockchain/block/blockListPage.scala.html @@ -18,7 +18,7 @@ @numberOfTxs.getOrElse(block.height, 0) diff --git a/app/views/component/blockchain/feeGrantAllowance.scala.html b/app/views/component/blockchain/feeGrantAllowance.scala.html index a50bc5d7c..73a4dc0c5 100644 --- a/app/views/component/blockchain/feeGrantAllowance.scala.html +++ b/app/views/component/blockchain/feeGrantAllowance.scala.html @@ -8,27 +8,25 @@ case constants.Blockchain.FeeGrant.BASIC_ALLOWANCE => { @defining(allowance.value.asInstanceOf[BasicAllowance]) { basicAllowance => @commonTextInfo(constants.View.SPEND_LIMITS, basicAllowance.spendLimit.map(_.getAmountWithNormalizedDenom()).mkString(", ")) - @commonTextInfo(constants.View.EXPIRATION, basicAllowance.expiration.getOrElse(""), 'id -> s"basicAllowance_${index}") - @if(basicAllowance.expiration.nonEmpty) { - + @basicAllowance.expiration.fold { + @commonTextInfo(constants.View.EXPIRATION, constants.View.NO_EXPIRY_DATE) + } { expiry => + @commonRFC3339Info(constants.View.EXPIRATION, expiry, s"basicAllowanceExpiry_${index}") } } } case constants.Blockchain.FeeGrant.PERIODIC_ALLOWANCE => { @defining(allowance.value.asInstanceOf[PeriodicAllowance]) { periodicAllowance => @commonTextInfo(constants.View.SPEND_LIMITS, periodicAllowance.basicAllowance.spendLimit.map(_.getAmountWithNormalizedDenom()).mkString(", ")) - @commonTextInfo(constants.View.EXPIRATION, periodicAllowance.basicAllowance.expiration.getOrElse(""), 'id -> s"basicAllowance_${index}") - @if(periodicAllowance.basicAllowance.expiration.nonEmpty) { - + @periodicAllowance.basicAllowance.expiration.fold { + @commonTextInfo(constants.View.EXPIRATION, constants.View.NO_EXPIRY_DATE) + } { expiry => + @commonRFC3339Info(constants.View.EXPIRATION, expiry, s"basicAllowanceExpiry_${index}") } @commonTextInfo(constants.View.PERIOD, periodicAllowance.period) @commonTextInfo(constants.View.PERIOD_SPEND_LIMIT, periodicAllowance.periodSpendLimit.map(_.getAmountWithNormalizedDenom()).mkString(", ")) @commonTextInfo(constants.View.PERIOD_CAN_SPEND, periodicAllowance.periodCanSpend.map(_.getAmountWithNormalizedDenom()).mkString(", ")) - @commonTextInfo(constants.View.PERIOD_RESET, periodicAllowance.periodReset) + @commonRFC3339Info(constants.View.PERIOD_RESET, periodicAllowance.periodReset, "periodicAllowancePeriodReset") } } case constants.Blockchain.FeeGrant.ALLOWED_MSG_ALLOWANCE => { diff --git a/app/views/component/blockchain/latestBlockHeight.scala.html b/app/views/component/blockchain/latestBlockHeight.scala.html index b04c745db..545fec56a 100644 --- a/app/views/component/blockchain/latestBlockHeight.scala.html +++ b/app/views/component/blockchain/latestBlockHeight.scala.html @@ -1,6 +1,7 @@ @import views.html.base._ +@import utilities.Date.RFC3339 -@(blockHeight: Int, proposer: String, time: String, averageBlockTime: Double, chainID: String)(implicit requestHeader: RequestHeader, messagesProvider: MessagesProvider) +@(blockHeight: Int, proposer: String, time: RFC3339, averageBlockTime: Double, chainID: String)(implicit requestHeader: RequestHeader, messagesProvider: MessagesProvider) @commonCard(constants.View.LATEST_BLOCK_HEIGHT) { } { @@ -31,5 +32,5 @@ } \ No newline at end of file diff --git a/app/views/component/blockchain/proposal/proposalDetails.scala.html b/app/views/component/blockchain/proposal/proposalDetails.scala.html index c473b3a12..4607731e0 100644 --- a/app/views/component/blockchain/proposal/proposalDetails.scala.html +++ b/app/views/component/blockchain/proposal/proposalDetails.scala.html @@ -12,11 +12,11 @@ @commonTextInfo(constants.View.DESCRIPTION, proposal.content.description) @commonTextInfo(constants.View.TYPE, proposal.content.proposalContentType) @commonTextInfo(constants.View.STATUS, Messages(proposal.status)) - @commonTextInfo(constants.View.SUBMIT_TIME, utilities.Date.bcTimestampToString(proposal.submitTime)) - @commonTextInfo(constants.View.DEPOSIT_END_TIME, proposal.depositEndTime) - @commonTextInfo(constants.View.TOTAL_DEPOSIT, proposal.totalDeposit.map(x => s"${x.amount.toMicroString} ${x.denom}").mkString(",")) - @commonTextInfo(constants.View.VOTING_START, proposal.votingStartTime) - @commonTextInfo(constants.View.VOTING_END, proposal.votingEndTime) + @commonRFC3339Info(constants.View.SUBMIT_TIME, proposal.submitTime, "proposalSubmitTime") + @commonRFC3339Info(constants.View.DEPOSIT_END_TIME, proposal.depositEndTime, "proposalDepositEndTime") + @commonTextInfo(constants.View.TOTAL_DEPOSIT, proposal.totalDeposit.map(_.getAmountWithNormalizedDenom()).mkString(", ")) + @commonRFC3339Info(constants.View.VOTING_START, proposal.votingStartTime, "proposalVotingStartTime") + @commonRFC3339Info(constants.View.VOTING_END, proposal.votingEndTime, "proposalVotingEndTime")
@Messages(constants.View.FINAL_TALLY_RESULTS): @commonTextInfo(constants.View.YES, proposal.finalTallyResult.yes.toString()) @@ -43,7 +43,7 @@ @defining(proposal.content.asInstanceOf[SoftwareUpgrade])(softwareUpgrade => { commonTextInfo(constants.View.NAME, softwareUpgrade.plan.name) commonTextInfo(constants.View.HEIGHT, softwareUpgrade.plan.height) - commonTextInfo(constants.View.TIME, softwareUpgrade.plan.time) + commonRFC3339Info(constants.View.TIME, softwareUpgrade.plan.time, "softwareUpgradePlanTime") commonTextInfo(constants.View.INFO, softwareUpgrade.plan.info) }) } diff --git a/app/views/component/blockchain/proposal/proposalList.scala.html b/app/views/component/blockchain/proposal/proposalList.scala.html index 7943a2eb5..94692301f 100644 --- a/app/views/component/blockchain/proposal/proposalList.scala.html +++ b/app/views/component/blockchain/proposal/proposalList.scala.html @@ -27,12 +27,12 @@ @Messages(proposal.status) @proposal.totalDeposit.map(x => s"${x.amount.toMicroString} ${x.denom}").mkString(",") diff --git a/app/views/component/blockchain/transaction/transactionDetails.scala.html b/app/views/component/blockchain/transaction/transactionDetails.scala.html index df5e09c6f..01ca11e72 100644 --- a/app/views/component/blockchain/transaction/transactionDetails.scala.html +++ b/app/views/component/blockchain/transaction/transactionDetails.scala.html @@ -8,7 +8,7 @@ @commonTextInfo(constants.View.TRANSACTION_HASH, transaction.hash) @commonHtmlInfo(constants.View.HEIGHT, commonJavascriptLink(transaction.height.toString, JavaScript(s"componentResource('explorerContent', jsRoutes.controllers.ComponentViewController.block(${transaction.height}))"))) @commonBooleanInfo(constants.View.STATUS, Option(transaction.status)) - @commonTextInfo(constants.View.TIME, "" ,'id -> "transactionDetailsTime") + @commonRFC3339Info(constants.View.TIME, transaction.timestamp, "transactionDetailsTime") @commonTextInfo(constants.View.TRANSACTION_FEES, if(transaction.fee.amount.nonEmpty) transaction.fee.amount.map(amt => amt.getAmountWithNormalizedDenom()).mkString(", ") else "0") @commonIntInfo(constants.View.NUMBER_OF_MESSAGES, transaction.messages.length) @if(!transaction.status) { @@ -16,8 +16,4 @@ @commonTextInfo(constants.View.LOG, transaction.rawLog) } @commonTextInfo(constants.View.MEMO, transaction.memo) -} - - \ No newline at end of file +} \ No newline at end of file diff --git a/app/views/component/blockchain/transaction/transactionListPage.scala.html b/app/views/component/blockchain/transaction/transactionListPage.scala.html index ebe3ec1d7..4feafa637 100644 --- a/app/views/component/blockchain/transaction/transactionListPage.scala.html +++ b/app/views/component/blockchain/transaction/transactionListPage.scala.html @@ -44,7 +44,7 @@ diff --git a/app/views/component/blockchain/txMessages/cosmos/grantAuthorization.scala.html b/app/views/component/blockchain/txMessages/cosmos/grantAuthorization.scala.html index b3d004678..a7470d99c 100644 --- a/app/views/component/blockchain/txMessages/cosmos/grantAuthorization.scala.html +++ b/app/views/component/blockchain/txMessages/cosmos/grantAuthorization.scala.html @@ -8,9 +8,6 @@ @defining(msg.asInstanceOf[GrantAuthorization]) { grantAuthorization => @commonHtmlInfo(constants.View.GRANTER, commonJavascriptLink(grantAuthorization.granter, JavaScript(s"componentResource('explorerContent', jsRoutes.controllers.ComponentViewController.wallet('${grantAuthorization.granter}'))"))) @commonHtmlInfo(constants.View.GRANTEE, commonJavascriptLink(grantAuthorization.grantee, JavaScript(s"componentResource('explorerContent', jsRoutes.controllers.ComponentViewController.wallet('${grantAuthorization.grantee}'))"))) - @commonTextInfo(constants.View.EXPIRATION, "", 'id -> s"grantAuthorization_${index}") + @commonRFC3339Info(constants.View.EXPIRATION, grantAuthorization.grant.expiration, s"grantAuthorization_${index}") @authorization(grantAuthorization.grant.authorization) - } \ No newline at end of file diff --git a/app/views/component/blockchain/txMessages/cosmos/submitEvidence.scala.html b/app/views/component/blockchain/txMessages/cosmos/submitEvidence.scala.html index c31b3c8a7..362c4a28b 100644 --- a/app/views/component/blockchain/txMessages/cosmos/submitEvidence.scala.html +++ b/app/views/component/blockchain/txMessages/cosmos/submitEvidence.scala.html @@ -8,7 +8,7 @@ @commonHtmlInfo(constants.View.SUBMITTER, commonJavascriptLink(submitEvidence.submitter, JavaScript(s"componentResource('explorerContent', jsRoutes.controllers.ComponentViewController.wallet('${submitEvidence.submitter}'))")))
@Messages(constants.View.EVIDENCE): @commonTextInfo(constants.View.HEIGHT, submitEvidence.evidence.height.toString) - @commonTextInfo(constants.View.TIME, utilities.Date.bcTimestampToString(submitEvidence.evidence.time)) + @commonRFC3339Info(constants.View.TIME, submitEvidence.evidence.time, "submitEvidenceTime") @commonTextInfo(constants.View.POWER, submitEvidence.evidence.power) @commonTextInfo(constants.View.CONSENSUS_ADDRESS, submitEvidence.evidence.consensusAddress)
diff --git a/app/views/component/blockchain/validator/validatorTransactionsPerPage.scala.html b/app/views/component/blockchain/validator/validatorTransactionsPerPage.scala.html index b7f8f50a7..1c542c004 100644 --- a/app/views/component/blockchain/validator/validatorTransactionsPerPage.scala.html +++ b/app/views/component/blockchain/validator/validatorTransactionsPerPage.scala.html @@ -38,7 +38,7 @@ diff --git a/conf/messages.en b/conf/messages.en index 4a777c684..8c7fede87 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -671,6 +671,7 @@ ID=ID DATA_NAME=Data Name FROM_ID=From ID TO_ID=To ID +NO_EXPIRY_DATE=Never expires! DENOM=Denom DATA_TYPE=Type ADD_IMMUTABLE_META_FIELD=Add Immutable Meta Field From 8129f48797b7f0db50f20ee7e71a3e9c3d3c15db Mon Sep 17 00:00:00 2001 From: avkr003 Date: Fri, 28 Jan 2022 16:16:25 +0530 Subject: [PATCH 2/2] self review correction --- app/models/common/FeeGrant.scala | 2 +- app/queries/responses/common/Header.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/common/FeeGrant.scala b/app/models/common/FeeGrant.scala index 93b48a542..105415d1b 100644 --- a/app/models/common/FeeGrant.scala +++ b/app/models/common/FeeGrant.scala @@ -54,7 +54,7 @@ object FeeGrant { val (_, isNeg) = utilities.Blockchain.subtractCoins(fromCoins = this.basicAllowance.spendLimit, amount = this.periodSpendLimit) val resetPeriodCanSpend = if (isNeg && this.basicAllowance.spendLimit.nonEmpty) this.basicAllowance.spendLimit else this.periodSpendLimit val updatedPeriodReset = { - val addPeriod = blockTime.addEpoch(utilities.Date.getEpoch(this.period)) + val addPeriod = this.periodReset.addEpoch(utilities.Date.getEpoch(this.period)) if (blockTime.isAfter(addPeriod)) blockTime.addEpoch(utilities.Date.getEpoch(this.period)) else addPeriod } (resetPeriodCanSpend, updatedPeriodReset) diff --git a/app/queries/responses/common/Header.scala b/app/queries/responses/common/Header.scala index 193987d4f..e976a0f43 100644 --- a/app/queries/responses/common/Header.scala +++ b/app/queries/responses/common/Header.scala @@ -8,12 +8,12 @@ case class Header(chain_id: String, height: Int, time: RFC3339, data_hash: Strin object Header { - def apply2(chain_id: String, height: String, time: String, data_hash: String, evidence_hash: String, validators_hash: String, proposer_address: String): Header = new Header(chain_id, height.toInt, RFC3339(time), data_hash, evidence_hash, validators_hash, proposer_address) + def apply2(chain_id: String, height: String, time: RFC3339, data_hash: String, evidence_hash: String, validators_hash: String, proposer_address: String): Header = new Header(chain_id, height.toInt, time, data_hash, evidence_hash, validators_hash, proposer_address) implicit val headerReads: Reads[Header] = ( (JsPath \ "chain_id").read[String] and (JsPath \ "height").read[String] and - (JsPath \ "time").read[String] and + (JsPath \ "time").read[RFC3339] and (JsPath \ "data_hash").read[String] and (JsPath \ "evidence_hash").read[String] and (JsPath \ "validators_hash").read[String] and