Skip to content

Commit

Permalink
LF: Move Speedy Interpretation Error to transaction package
Browse files Browse the repository at this point in the history
to be accessible from any package

part of #9974

CHANGELOG_BEGIN
CHANGELOG_END
  • Loading branch information
remyhaemmerle-da committed Jun 24, 2021
1 parent 01e329f commit 30cddb5
Show file tree
Hide file tree
Showing 25 changed files with 435 additions and 367 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.scenario
package com.daml.lf
package scenario

import com.daml.lf.data.{ImmArray, Numeric, Ref}
import com.daml.lf.ledger.EventId
Expand Down Expand Up @@ -88,58 +89,76 @@ final class Conversions(

case SError.SRequiresOnLedger(operation) => setCrash(operation)

case SError.DamlEMatchError(reason) => setCrash(reason)

case SError.DamlEUnhandledException(excep) =>
builder.setUnhandledException(convertValue(excep.value.toValue))

case SError.DamlEUserError(msg) => builder.setUserError(msg)

case SError.DamlETransactionError(reason) =>
setCrash(reason)

case SError.DamlETemplatePreconditionViolated(tid, optLoc, arg) =>
val uepvBuilder = proto.ScenarioError.TemplatePreconditionViolated.newBuilder
optLoc.map(convertLocation).foreach(uepvBuilder.setLocation)
builder.setTemplatePrecondViolated(
uepvBuilder
.setTemplateId(convertIdentifier(tid))
.setArg(convertValue(arg))
.build
)
case SError.DamlELocalContractNotActive(coid, tid, consumedBy) =>
builder.setUpdateLocalContractNotActive(
proto.ScenarioError.ContractNotActive.newBuilder
.setContractRef(mkContractRef(coid, tid))
.setConsumedBy(proto.NodeId.newBuilder.setId(consumedBy.toString).build)
.build
)
case SError.DamlEDuplicateContractKey(key) =>
builder.setScenarioCommitError(
proto.CommitError.newBuilder.setUniqueKeyViolation(convertGlobalKey(key)).build
)
case SError.DamlEFailedAuthorization(nid, fa) =>
builder.setScenarioCommitError(
proto.CommitError.newBuilder
.setFailedAuthorizations(convertFailedAuthorization(nid, fa))
.build
)

case SError.DamlECreateEmptyContractKeyMaintainers(tid, arg, key) =>
builder.setCreateEmptyContractKeyMaintainers(
proto.ScenarioError.CreateEmptyContractKeyMaintainers.newBuilder
.setArg(convertValue(arg))
.setTemplateId(convertIdentifier(tid))
.setKey(convertValue(key))
)

case SError.DamlEFetchEmptyContractKeyMaintainers(tid, key) =>
builder.setFetchEmptyContractKeyMaintainers(
proto.ScenarioError.FetchEmptyContractKeyMaintainers.newBuilder
.setTemplateId(convertIdentifier(tid))
.setKey(convertValue(key))
)
case SError.SErrorDamlException(interpretationError) =>
import interpretation.Error._
interpretationError match {
case UnhandledException(_, value) =>
builder.setUnhandledException(convertValue(value))
case UserError(msg) =>
builder.setUserError(msg)
case ContractNotFound(cid) =>
setCrash(s"contract ${cid.coid} not found")
case TemplatePreconditionViolated(tid, optLoc, arg) =>
val uepvBuilder = proto.ScenarioError.TemplatePreconditionViolated.newBuilder
optLoc.map(convertLocation).foreach(uepvBuilder.setLocation)
builder.setTemplatePrecondViolated(
uepvBuilder
.setTemplateId(convertIdentifier(tid))
.setArg(convertValue(arg))
.build
)
case LocalContractNotActive(coid, tid, consumedBy) =>
builder.setUpdateLocalContractNotActive(
proto.ScenarioError.ContractNotActive.newBuilder
.setContractRef(mkContractRef(coid, tid))
.setConsumedBy(proto.NodeId.newBuilder.setId(consumedBy.toString).build)
.build
)
case LocalContractKeyNotVisible(coid, gk, actAs, readAs, stakeholders) =>
builder.setScenarioContractKeyNotVisible(
proto.ScenarioError.ContractKeyNotVisible.newBuilder
.setContractRef(mkContractRef(coid, gk.templateId))
.addAllActAs(actAs.map(convertParty(_)).asJava)
.addAllReadAs(readAs.map(convertParty(_)).asJava)
.addAllStakeholders(stakeholders.map(convertParty).asJava)
.build
)
case ContractKeyNotFound(gk) =>
builder.setScenarioContractKeyNotFound(
proto.ScenarioError.ContractKeyNotFound.newBuilder
.setTemplateId(convertIdentifier(gk.templateId))
.setKey(convertValue(gk.key))
.build
)
case DuplicateContractKey(key) =>
builder.setScenarioCommitError(
proto.CommitError.newBuilder.setUniqueKeyViolation(convertGlobalKey(key)).build
)
case CreateEmptyContractKeyMaintainers(tid, arg, key) =>
builder.setCreateEmptyContractKeyMaintainers(
proto.ScenarioError.CreateEmptyContractKeyMaintainers.newBuilder
.setArg(convertValue(arg))
.setTemplateId(convertIdentifier(tid))
.setKey(convertValue(key))
)
case FetchEmptyContractKeyMaintainers(tid, key) =>
builder.setFetchEmptyContractKeyMaintainers(
proto.ScenarioError.FetchEmptyContractKeyMaintainers.newBuilder
.setTemplateId(convertIdentifier(tid))
.setKey(convertValue(key))
)
case wtc: WronglyTypedContract =>
sys.error(
s"Got unexpected DamlEWronglyTypedContract error in scenario service: $wtc. Note that in the scenario service this error should never surface since contract fetches are all type checked."
)
case FailedAuthorization(nid, fa) =>
builder.setScenarioCommitError(
proto.CommitError.newBuilder
.setFailedAuthorizations(convertFailedAuthorization(nid, fa))
.build
)

}
case SError.ScenarioErrorContractNotEffective(coid, tid, effectiveAt) =>
builder.setScenarioContractNotEffective(
proto.ScenarioError.ContractNotEffective.newBuilder
Expand All @@ -166,16 +185,6 @@ final class Conversions(
.build
)

case SError.DamlELocalContractKeyNotVisible(coid, gk, actAs, readAs, stakeholders) =>
builder.setScenarioContractKeyNotVisible(
proto.ScenarioError.ContractKeyNotVisible.newBuilder
.setContractRef(mkContractRef(coid, gk.templateId))
.addAllActAs(actAs.map(convertParty(_)).asJava)
.addAllReadAs(readAs.map(convertParty(_)).asJava)
.addAllStakeholders(stakeholders.map(convertParty).asJava)
.build
)

case SError.ScenarioErrorContractKeyNotVisible(coid, gk, actAs, readAs, stakeholders) =>
builder.setScenarioContractKeyNotVisible(
proto.ScenarioError.ContractKeyNotVisible.newBuilder
Expand All @@ -187,14 +196,6 @@ final class Conversions(
.build
)

case SError.DamlEContractKeyNotFound(gk) =>
builder.setScenarioContractKeyNotFound(
proto.ScenarioError.ContractKeyNotFound.newBuilder
.setTemplateId(convertIdentifier(gk.templateId))
.setKey(convertValue(gk.key))
.build
)

case SError.ScenarioErrorCommitError(commitError) =>
builder.setScenarioCommitError(
convertCommitError(commitError)
Expand All @@ -207,11 +208,6 @@ final class Conversions(

case SError.ScenarioErrorPartyAlreadyExists(party) =>
builder.setScenarioPartyAlreadyExists(party)

case wtc: SError.DamlEWronglyTypedContract =>
sys.error(
s"Got unexpected DamlEWronglyTypedContract error in scenario service: $wtc. Note that in the scenario service this error should never surface since contract fetches are all type checked."
)
}
builder.build
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,24 +321,23 @@ class Engine(val config: EngineConfig = new EngineConfig(LanguageVersion.StableV
time: Time.Timestamp,
): Result[(SubmittedTransaction, Tx.Metadata)] = machine.withOnLedger("Daml Engine") { onLedger =>
var finished: Boolean = false
def detailMsg =
s"Last location: ${Pretty.prettyLoc(machine.lastLocation).render(80)}, partial transaction: ${onLedger.ptxInternal.nodesToString}"
while (!finished) {
machine.run() match {
case SResultFinalValue(_) => finished = true

case SResultError(SError.DamlEDuplicateContractKey(key)) =>
case SResultError(SError.SErrorDamlException(error)) =>
// Special-cased because duplicate key errors
// produce a different gRPC error code.
return ResultError(Error.Interpretation(Error.Interpretation.DuplicateContractKey(key)))

case SResultError(SError.DamlEContractKeyNotFound(key)) =>
return ResultError(Error.Interpretation.ContractKeyNotFound(key))
return ResultError(Error.Interpretation.DamlException(error), detailMsg)

case SResultError(err) =>
return ResultError(
Error.Interpretation.Generic(
s"Interpretation error: ${Pretty.prettyError(err, onLedger.ptxInternal).render(80)}",
s"Last location: ${Pretty.prettyLoc(machine.lastLocation).render(80)}, partial transaction: ${onLedger.ptxInternal.nodesToString}",
)
s"Interpretation error: ${Pretty.prettyError(err, onLedger.ptxInternal).render(80)}"
),
detailMsg,
)

case SResultNeedPackage(pkgId, callback) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package engine

import com.daml.lf.data.Ref
import com.daml.lf.language.Ast
import com.daml.lf.transaction.{GlobalKey, NodeId}
import com.daml.lf.transaction.NodeId
import com.daml.lf.value.Value

sealed abstract class Error {
Expand Down Expand Up @@ -125,7 +125,8 @@ object Error {
}

// Error happening during interpretation
final case class Interpretation(interpretationError: Interpretation.Error) extends Error {
final case class Interpretation(interpretationError: Interpretation.Error, detailMsg: String)
extends Error {
def msg: String = interpretationError.msg
}

Expand All @@ -137,28 +138,22 @@ object Error {

// TODO https://github.com/digital-asset/daml/issues/9974
// get rid of Generic
final case class Generic(override val msg: String, detailMsg: String) extends Error
object Generic {
def apply(msg: String): Generic = Generic(msg, msg)
final case class Generic(override val msg: String) extends Error

final case class DamlException(error: interpretation.Error) extends Error {
// TODO https://github.com/digital-asset/daml/issues/9974
// For now we try to preserve the excat same messge (for the ledger API)
// Review once all the errors are properly structured
override def msg: String = error match {
case interpretation.Error.ContractNotFound(cid) =>
s"Contract could not be found with id ${cid.coid}"
case interpretation.Error.ContractKeyNotFound(key) =>
s"dependency error: couldn't find key: $key"
case _ =>
s"Interpretation error: Error: ${speedy.Pretty.prettyDamlException(error).render(80)}"
}
}

final case class ContractNotFound(ci: Value.ContractId) extends Error {
override def msg = s"Contract could not be found with id $ci"
}

final case class ContractKeyNotFound(key: GlobalKey) extends Error {
override def msg = s"dependency error: couldn't find key: $key"
}

/** See com.daml.lf.transaction.Transaction.DuplicateContractKey
* for more information.
*/
final case class DuplicateContractKey(key: GlobalKey) extends Error {
override def msg = s"Duplicate contract key $key"
}

final case class Authorization(override val msg: String) extends Error

}

// Error happening during transaction validation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.engine
package com.daml.lf
package engine

import com.daml.lf.data.Ref._
import com.daml.lf.data.{BackStack, ImmArray, ImmArrayCons}
Expand Down Expand Up @@ -75,8 +76,8 @@ object ResultError {
ResultError(Error.Package(packageError))
def apply(preprocessingError: Error.Preprocessing.Error): ResultError =
ResultError(Error.Preprocessing(preprocessingError))
def apply(interpretationError: Error.Interpretation.Error): ResultError =
ResultError(Error.Interpretation(interpretationError))
def apply(interpretationError: Error.Interpretation.Error, details: String = "N/A"): ResultError =
ResultError(Error.Interpretation(interpretationError, details))
def apply(validationError: Error.Validation.Error): ResultError =
ResultError(Error.Validation(validationError))
}
Expand Down Expand Up @@ -165,7 +166,10 @@ object Result {
ResultNeedContract(
acoid,
{
case None => ResultError(Error.Interpretation.ContractNotFound(acoid))
case None =>
ResultError(
Error.Interpretation.DamlException(interpretation.Error.ContractNotFound(acoid))
)
case Some(contract) => resume(contract)
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import Value._
import com.daml.lf.speedy.{InitialSeeding, SValue, svalue}
import com.daml.lf.speedy.SValue._
import com.daml.lf.command._
import com.daml.lf.engine.Error.Interpretation
import com.daml.lf.transaction.Node.{GenActionNode, GenNode}
import com.daml.lf.transaction.test.TransactionBuilder.assertAsVersionedValue
import org.scalactic.Equality
Expand Down Expand Up @@ -754,9 +755,9 @@ class EngineTest
val submitResult = engine
.submit(submitters, Commands(ImmArray(command), let, "test"), participant, submissionSeed)
.consume(lookupContract, lookupPackage, lookupKey, VisibleByKey.fromSubmitters(submitters))
inside(submitResult) { case Left(err) =>
err shouldBe Error.Interpretation(
Error.Interpretation.ContractKeyNotFound(
inside(submitResult) { case Left(Error.Interpretation(err, _)) =>
err shouldBe Interpretation.DamlException(
interpretation.Error.ContractKeyNotFound(
GlobalKey.assertBuild(
BasicTests_WithKey,
ValueRecord(
Expand Down Expand Up @@ -968,21 +969,22 @@ class EngineTest
)
.consume(_ => None, lookupPackage, lookupKey, VisibleByKey.fromSubmitters(submitters))

inside(result) { case Left(err) =>
err shouldBe Error.Interpretation(
Error.Interpretation.ContractKeyNotFound(
GlobalKey.assertBuild(
BasicTests_WithKey,
ValueRecord(
Some(BasicTests_WithKey),
ImmArray(
(Some[Ref.Name]("p"), ValueParty(alice)),
(Some[Ref.Name]("k"), ValueInt64(43)),
inside(result) { case Left(Error.Interpretation(err, _)) =>
err shouldBe
Interpretation.DamlException(
interpretation.Error.ContractKeyNotFound(
GlobalKey.assertBuild(
BasicTests_WithKey,
ValueRecord(
Some(BasicTests_WithKey),
ImmArray(
(Some[Ref.Name]("p"), ValueParty(alice)),
(Some[Ref.Name]("k"), ValueInt64(43)),
),
),
),
)
)
)
)
}
}
"error if Speedy fails to find the key" in {
Expand Down Expand Up @@ -1013,21 +1015,22 @@ class EngineTest
)
.consume(_ => None, lookupPackage, lookupKey, VisibleByKey.fromSubmitters(submitters))

inside(result) { case Left(err) =>
err shouldBe Error.Interpretation(
Error.Interpretation.ContractKeyNotFound(
GlobalKey.assertBuild(
BasicTests_WithKey,
ValueRecord(
Some(BasicTests_WithKey),
ImmArray(
(Some[Ref.Name]("p"), ValueParty(alice)),
(Some[Ref.Name]("k"), ValueInt64(43)),
inside(result) { case Left(Error.Interpretation(err, _)) =>
err shouldBe
Interpretation.DamlException(
interpretation.Error.ContractKeyNotFound(
GlobalKey.assertBuild(
BasicTests_WithKey,
ValueRecord(
Some(BasicTests_WithKey),
ImmArray(
(Some[Ref.Name]("p"), ValueParty(alice)),
(Some[Ref.Name]("k"), ValueInt64(43)),
),
),
),
)
)
)
)
}
}
}
Expand Down
Loading

0 comments on commit 30cddb5

Please sign in to comment.