From 4e632999194d89b74bd328393ef72f8c71b5ccaf Mon Sep 17 00:00:00 2001 From: Moritz Kiefer Date: Mon, 17 May 2021 13:38:27 +0200 Subject: [PATCH] Simplify kvutils contract keys validation (#9628) changelog_begin changelog_end --- .../keys/ContractKeysValidation.scala | 143 +++++------------- .../keys/KeyConsistencyValidation.scala | 109 ------------- .../keys/KeyUniquenessValidation.scala | 41 ----- 3 files changed, 35 insertions(+), 258 deletions(-) delete mode 100644 ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyConsistencyValidation.scala delete mode 100644 ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyUniquenessValidation.scala diff --git a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/ContractKeysValidation.scala b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/ContractKeysValidation.scala index 2603c40778..73befaf61d 100644 --- a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/ContractKeysValidation.scala +++ b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/ContractKeysValidation.scala @@ -3,6 +3,7 @@ package com.daml.ledger.participant.state.kvutils.committer.transaction.keys +import com.daml.ledger.participant.state.kvutils.Conversions import com.daml.ledger.participant.state.kvutils.DamlKvutils.{ DamlContractKey, DamlStateKey, @@ -13,14 +14,10 @@ import com.daml.ledger.participant.state.kvutils.committer.transaction.{ Step, TransactionCommitter, } -import com.daml.ledger.participant.state.kvutils.committer.transaction.keys.KeyConsistencyValidation.checkNodeKeyConsistency import com.daml.ledger.participant.state.kvutils.committer.transaction.keys.KeyMonotonicityValidation.checkContractKeysCausalMonotonicity -import com.daml.ledger.participant.state.kvutils.committer.transaction.keys.KeyUniquenessValidation.checkNodeKeyUniqueness import com.daml.ledger.participant.state.kvutils.committer.{CommitContext, StepContinue, StepResult} import com.daml.ledger.participant.state.v1.RejectionReason import com.daml.lf.data.Time.Timestamp -import com.daml.lf.transaction.{Node, NodeId} -import com.daml.lf.value.Value.ContractId import com.daml.logging.LoggingContext private[transaction] object ContractKeysValidation { @@ -55,7 +52,6 @@ private[transaction] object ContractKeysValidation { finalState <- performTraversalContractKeysChecks( transactionCommitter, commitContext.recordTime, - contractKeyDamlStateKeys, contractKeysToContractIds, stateAfterMonotonicityCheck, ) @@ -66,31 +62,43 @@ private[transaction] object ContractKeysValidation { private def performTraversalContractKeysChecks( transactionCommitter: TransactionCommitter, recordTime: Option[Timestamp], - contractKeyDamlStateKeys: Set[DamlStateKey], contractKeysToContractIds: Map[DamlContractKey, RawContractId], transactionEntry: DamlTransactionEntrySummary, )(implicit loggingContext: LoggingContext): StepResult[DamlTransactionEntrySummary] = { - type KeyValidationStackStatus = Either[KeyValidationError, List[KeyValidationState]] + import scalaz.std.either._ + import scalaz.std.list._ + import scalaz.syntax.foldable._ - val keysValidationOutcome = transactionEntry.transaction - .foldInExecutionOrder[KeyValidationStackStatus]( - Right(List(KeyValidationState(activeStateKeys = contractKeyDamlStateKeys))) - )( - exerciseBegin = (status, _, exerciseBeginNode) => { - val newStatus = - onCurrentStatus(status)( - checkNodeContractKey(exerciseBeginNode, contractKeysToContractIds, _) - ) - (newStatus, true) - }, - rollbackBegin = (status, _, _) => - // Store state to restore after rollback - (status.map(stack => stack.head +: stack), true), - leaf = (status, _, leafNode) => - onCurrentStatus(status)(checkNodeContractKey(leafNode, contractKeysToContractIds, _)), - exerciseEnd = (accum, _, _) => accum, - rollbackEnd = (status, _, _) => status.map(stack => stack.tail), - ) + import com.daml.lf.transaction.Transaction.{ + KeyActive, + KeyCreate, + NegativeKeyLookup, + DuplicateKeys, + InconsistentKeys, + } + + val transaction = transactionEntry.transaction + + val keysValidationOutcome = for { + keyInputs <- transaction.contractKeyInputs.left.map { + case DuplicateKeys(_) => Duplicate + case InconsistentKeys(_) => Inconsistent + } + _ <- keyInputs.toList.traverse_ { case (key, keyInput) => + val submittedDamlContractKey = Conversions.encodeGlobalKey(key) + (contractKeysToContractIds.get(submittedDamlContractKey), keyInput) match { + case (Some(_), KeyCreate) => Left(Duplicate) + case (Some(_), NegativeKeyLookup) => Left(Inconsistent) + case (Some(cid), KeyActive(submitted)) => + if (cid != submitted.coid) + Left(Inconsistent) + else + Right(()) + case (None, KeyActive(_)) => Left(Inconsistent) + case (None, KeyCreate | NegativeKeyLookup) => Right(()) + } + } + } yield () keysValidationOutcome match { case Right(_) => @@ -112,90 +120,9 @@ private[transaction] object ContractKeysValidation { } } - private def checkNodeContractKey( - node: Node.GenActionNode[NodeId, ContractId], - contractKeysToContractIds: Map[DamlContractKey, RawContractId], - initialState: KeyValidationState, - ): KeyValidationStatus = - for { - stateAfterUniquenessCheck <- initialState.onActiveStateKeys( - checkNodeKeyUniqueness( - node, - _, - ) - ) - finalState <- stateAfterUniquenessCheck.onSubmittedContractKeysToContractIds( - checkNodeKeyConsistency( - contractKeysToContractIds, - node, - _, - ) - ) - } yield finalState - private[keys] type RawContractId = String - private[keys] sealed trait KeyValidationError + private[keys] sealed trait KeyValidationError extends Product with Serializable private[keys] case object Duplicate extends KeyValidationError private[keys] case object Inconsistent extends KeyValidationError - - /** The state used during key validation. - * - * @param activeStateKeys The currently active contract keys for uniqueness - * checks starting with the keys active before the transaction. - * @param submittedContractKeysToContractIds Map of keys to their expected assignment at - * the beginning of the transaction starting with an empty map. - * E.g., a create (with nothing before) means the key must have been inactive - * at the beginning. - */ - private[keys] final class KeyValidationState private[ContractKeysValidation] ( - private[keys] val activeStateKeys: Set[DamlStateKey], - private[keys] val submittedContractKeysToContractIds: KeyConsistencyValidation.State, - ) { - - def onSubmittedContractKeysToContractIds( - f: KeyConsistencyValidation.State => KeyConsistencyValidation.Status - ): KeyValidationStatus = - f(submittedContractKeysToContractIds).map(newSubmitted => - new KeyValidationState( - activeStateKeys = this.activeStateKeys, - submittedContractKeysToContractIds = newSubmitted, - ) - ) - - def onActiveStateKeys( - f: Set[DamlStateKey] => Either[KeyValidationError, Set[DamlStateKey]] - ): KeyValidationStatus = - f(activeStateKeys).map(newActive => - new KeyValidationState( - activeStateKeys = newActive, - submittedContractKeysToContractIds = this.submittedContractKeysToContractIds, - ) - ) - } - - private[keys] object KeyValidationState { - def apply(activeStateKeys: Set[DamlStateKey]): KeyValidationState = - new KeyValidationState(activeStateKeys, Map.empty) - - def apply( - submittedContractKeysToContractIds: Map[DamlContractKey, Option[ - RawContractId - ]] - ): KeyValidationState = - new KeyValidationState(Set.empty, submittedContractKeysToContractIds) - } - - private[keys] type KeyValidationStatus = - Either[KeyValidationError, KeyValidationState] - - def onCurrentStatus[E, A]( - statusStack: Either[E, List[A]] - )(f: A => Either[E, A]): Either[E, List[A]] = - for { - statusStack <- statusStack - status <- f(statusStack.head) - } yield { - status +: statusStack.tail - } } diff --git a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyConsistencyValidation.scala b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyConsistencyValidation.scala deleted file mode 100644 index 9964840614..0000000000 --- a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyConsistencyValidation.scala +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package com.daml.ledger.participant.state.kvutils.committer.transaction.keys - -import com.daml.ledger.participant.state.kvutils.Conversions -import com.daml.ledger.participant.state.kvutils.DamlKvutils.DamlContractKey -import com.daml.ledger.participant.state.kvutils.committer.transaction.keys.ContractKeysValidation.{ - Inconsistent, - KeyValidationError, - RawContractId, -} -import com.daml.lf.data.Ref.TypeConName -import com.daml.lf.transaction.{Node, NodeId} -import com.daml.lf.value.Value -import com.daml.lf.value.Value.ContractId - -private[keys] object KeyConsistencyValidation { - type State = Map[DamlContractKey, Option[RawContractId]] - type Status = - Either[KeyValidationError, State] - - def checkNodeKeyConsistency( - contractKeysToContractIds: Map[DamlContractKey, RawContractId], - node: Node.GenActionNode[NodeId, ContractId], - submittedContractKeysToContractIds: Map[DamlContractKey, Option[RawContractId]], - ): Status = - node match { - case exercise: Node.NodeExercises[NodeId, ContractId] => - checkKeyConsistency( - contractKeysToContractIds, - exercise.key, - Some(exercise.targetCoid), - exercise.templateId, - submittedContractKeysToContractIds, - ) - - case create: Node.NodeCreate[ContractId] => - checkKeyConsistency( - contractKeysToContractIds, - create.key, - None, - create.templateId, - submittedContractKeysToContractIds, - ) - - case fetch: Node.NodeFetch[ContractId] => - checkKeyConsistency( - contractKeysToContractIds, - fetch.key, - Some(fetch.coid), - fetch.templateId, - submittedContractKeysToContractIds, - ) - - case lookupByKey: Node.NodeLookupByKey[ContractId] => - checkKeyConsistency( - contractKeysToContractIds, - Some(lookupByKey.key), - lookupByKey.result, - lookupByKey.templateId, - submittedContractKeysToContractIds, - ) - } - - private def checkKeyConsistency( - contractKeysToContractIds: Map[DamlContractKey, RawContractId], - key: Option[Node.KeyWithMaintainers[Value[ContractId]]], - targetContractId: Option[ContractId], - templateId: TypeConName, - submittedContractKeysToContractIds: Map[DamlContractKey, Option[RawContractId]], - ): Status = - key match { - case None => Right(submittedContractKeysToContractIds) - case Some(submittedKeyWithMaintainers) => - val submittedDamlContractKey = - Conversions.encodeContractKey(templateId, submittedKeyWithMaintainers.key) - val newSubmittedContractKeysToContractIds = - if (submittedContractKeysToContractIds.contains(submittedDamlContractKey)) { - submittedContractKeysToContractIds - } else { - submittedContractKeysToContractIds.updated( - submittedDamlContractKey, - targetContractId.map(_.coid), - ) - } - checkKeyConsistency( - contractKeysToContractIds, - submittedDamlContractKey, - newSubmittedContractKeysToContractIds, - ) - } - - private def checkKeyConsistency( - contractKeysToContractIds: Map[DamlContractKey, RawContractId], - submittedDamlContractKey: DamlContractKey, - submittedContractKeysToContractIds: Map[DamlContractKey, Option[RawContractId]], - ): Status = - if ( - submittedContractKeysToContractIds - .get( - submittedDamlContractKey - ) - .flatten != contractKeysToContractIds.get(submittedDamlContractKey) - ) - Left(Inconsistent) - else - Right(submittedContractKeysToContractIds) -} diff --git a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyUniquenessValidation.scala b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyUniquenessValidation.scala deleted file mode 100644 index f1533c183b..0000000000 --- a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/committer/transaction/keys/KeyUniquenessValidation.scala +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package com.daml.ledger.participant.state.kvutils.committer.transaction.keys - -import com.daml.ledger.participant.state.kvutils.Conversions -import com.daml.ledger.participant.state.kvutils.DamlKvutils.DamlStateKey -import com.daml.ledger.participant.state.kvutils.committer.transaction.keys.ContractKeysValidation.{ - Duplicate, - KeyValidationError, -} -import com.daml.lf.transaction.{Node, NodeId} -import com.daml.lf.value.Value.ContractId - -private[keys] object KeyUniquenessValidation { - - def checkNodeKeyUniqueness( - node: Node.GenNode[NodeId, ContractId], - activeStateKeys: Set[DamlStateKey], - ): Either[KeyValidationError, Set[DamlStateKey]] = - node match { - case exercise: Node.NodeExercises[NodeId, ContractId] - if exercise.key.isDefined && exercise.consuming => - val stateKey = - Conversions.contractKeyToStateKey(exercise.templateId, exercise.key.get.key) - Right( - activeStateKeys - stateKey - ) - - case create: Node.NodeCreate[ContractId] if create.key.isDefined => - val stateKey = - Conversions.contractKeyToStateKey(create.coinst.template, create.key.get.key) - - if (activeStateKeys.contains(stateKey)) - Left(Duplicate) - else - Right(activeStateKeys + stateKey) - - case _ => Right(activeStateKeys) - } -}