ifaces:support exercising by required interface (#13554)

* ifaces:support exercising by required interface

This adds support to exercise an interface choice on a contract ID,
where the interface is required by one of the implemented interfaces of
the contract template.

Fixes #13434.

CHANGELOG_BEGIN
CHANGELOG_END

* some improvements based on review

* added a test plus a bugfix

* Update daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/PhaseOne.scala

Co-authored-by: Sofia Faro <sofia.faro@digitalasset.com>

* 2 more interface tests for the command preprocessor

Co-authored-by: Sofia Faro <sofia.faro@digitalasset.com>
This commit is contained in:
Robin Krom 2022-04-12 20:15:29 +02:00 committed by GitHub
parent 4a3d0b316b
commit fcd3b6622b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 256 additions and 14 deletions

View File

@ -446,6 +446,22 @@ prettyScenarioErrorError (Just err) = do
(prettyDefName world) (prettyDefName world)
scenarioError_ContractDoesNotImplementInterfaceInterfaceId scenarioError_ContractDoesNotImplementInterfaceInterfaceId
] ]
ScenarioErrorErrorContractDoesNotImplementRequiringInterface ScenarioError_ContractDoesNotImplementRequiringInterface {..} ->
pure $ vcat
[ "Attempt to use a contract via a required interface, but the contract does not implement the requiring interface"
, label_ "Contract: " $
prettyMay "<missing contract>"
(prettyContractRef world)
scenarioError_ContractDoesNotImplementRequiringInterfaceContractRef
, label_ "Required interface: " $
prettyMay "<missing interface>"
(prettyDefName world)
scenarioError_ContractDoesNotImplementRequiringInterfaceRequiredInterfaceId
, label_ "Requiring interface: " $
prettyMay "<missing interface>"
(prettyDefName world)
scenarioError_ContractDoesNotImplementRequiringInterfaceRequiringInterfaceId
]
partyDifference :: V.Vector Party -> V.Vector Party -> Doc SyntaxClass partyDifference :: V.Vector Party -> V.Vector Party -> Doc SyntaxClass
partyDifference with without = partyDifference with without =

View File

@ -229,6 +229,12 @@ message ScenarioError {
Identifier interface_id = 2; Identifier interface_id = 2;
} }
message ContractDoesNotImplementRequiringInterface {
ContractRef contract_ref = 1;
Identifier requiring_interface_id = 2;
Identifier required_interface_id = 3;
}
// The state of the ledger at the time of the error // The state of the ledger at the time of the error
repeated ScenarioStep scenario_steps = 1; repeated ScenarioStep scenario_steps = 1;
repeated Node nodes = 2; repeated Node nodes = 2;
@ -297,6 +303,8 @@ message ScenarioError {
PartiesNotAllocated scenario_parties_not_allocated = 33; PartiesNotAllocated scenario_parties_not_allocated = 33;
ChoiceGuardFailed choice_guard_failed = 34; ChoiceGuardFailed choice_guard_failed = 34;
ContractDoesNotImplementInterface contract_does_not_implement_interface = 35; ContractDoesNotImplementInterface contract_does_not_implement_interface = 35;
ContractDoesNotImplementRequiringInterface contract_does_not_implement_requiring_interface
= 36;
} }
} }

View File

@ -171,6 +171,19 @@ final class Conversions(
.setInterfaceId(convertIdentifier(interfaceId)) .setInterfaceId(convertIdentifier(interfaceId))
.build .build
) )
case ContractDoesNotImplementRequiringInterface(
requiredIfaceId,
requiringIfaceId,
coid,
templateId,
) =>
builder.setContractDoesNotImplementRequiringInterface(
proto.ScenarioError.ContractDoesNotImplementRequiringInterface.newBuilder
.setContractRef(mkContractRef(coid, templateId))
.setRequiredInterfaceId(convertIdentifier(requiredIfaceId))
.setRequiringInterfaceId(convertIdentifier(requiringIfaceId))
.build
)
case FailedAuthorization(nid, fa) => case FailedAuthorization(nid, fa) =>
builder.setScenarioCommitError( builder.setScenarioCommitError(
proto.CommitError.newBuilder proto.CommitError.newBuilder

View File

@ -59,6 +59,12 @@ private[lf] final class CommandPreprocessor(
command(choice, speedy.Command.ExerciseInterface(identifier, cid, choiceId, _)) command(choice, speedy.Command.ExerciseInterface(identifier, cid, choiceId, _))
case ChoiceInfo.Inherited(ifaceId, choice) => case ChoiceInfo.Inherited(ifaceId, choice) =>
command(choice, speedy.Command.ExerciseByInterface(ifaceId, identifier, cid, choiceId, _)) command(choice, speedy.Command.ExerciseByInterface(ifaceId, identifier, cid, choiceId, _))
case ChoiceInfo.InterfaceInherited(ifaceId, choice) =>
command(
choice,
speedy.Command
.ExerciseByInheritedInterface(ifaceId, identifier, cid, choiceId, _),
)
} }
} }

View File

@ -45,6 +45,14 @@ class ApiCommandPreprocessorSpec
controllers Mod:Record {owners} this, controllers Mod:Record {owners} this,
observers Nil @Party observers Nil @Party
to create @Mod:Record Mod:Record { owners = Mod:Box @(List Party) {content} box, data = Mod:Record {data} this } ; to create @Mod:Record Mod:Record { owners = Mod:Box @(List Party) {content} box, data = Mod:Record {data} this } ;
implements Mod:Iface{
method getCtrls = Mod:Record {owners} this;
choice IfaceChoice;
};
implements Mod:Iface3{
method getCtrls = Mod:Record {owners} this;
choice IfaceChoice3;
};
key @(List Party) (Mod:Record {owners} this) (\ (parties: List Party) -> parties); key @(List Party) (Mod:Record {owners} this) (\ (parties: List Party) -> parties);
}; };
@ -62,6 +70,31 @@ class ApiCommandPreprocessorSpec
key @(List Party) (Mod:RecordRef {owners} this) (\ (parties: List Party) -> parties); key @(List Party) (Mod:RecordRef {owners} this) (\ (parties: List Party) -> parties);
}; };
interface (this: Iface) = {
requires Mod:Iface3;
precondition True;
method getCtrls: List Party;
choice IfaceChoice (self) (u:Unit) : Unit
, controllers (call_method @Mod:Iface getCtrls this)
to upure @Unit ();
} ;
interface (this: Iface2) = {
precondition True;
method getCtrls: List Party;
choice IfaceChoice2 (self) (u:Unit) : Unit
, controllers (call_method @Mod:Iface2 getCtrls this)
to upure @Unit ();
} ;
interface (this: Iface3) = {
precondition True;
method getCtrls: List Party;
choice IfaceChoice3 (self) (u:Unit) : Unit
, controllers (call_method @Mod:Iface3 getCtrls this)
to upure @Unit ();
} ;
} }
""" """
@ -96,6 +129,21 @@ class ApiCommandPreprocessorSpec
"Transfer", "Transfer",
ValueRecord("", ImmArray("content" -> ValueList(FrontStack(ValueParty("Clara"))))), ValueRecord("", ImmArray("content" -> ValueList(FrontStack(ValueParty("Clara"))))),
) )
// TEST_EVIDENCE: Input Validation: well formed exercise-by-interface command is accepted
val validExeByInterface = ApiCommand.Exercise(
"Mod:Iface",
newCid,
"IfaceChoice",
ValueUnit,
)
// TEST_EVIDENCE: Input Validation: well formed exercise-by-interface via required interface command is accepted
val validExeByRequiredInterface = ApiCommand.Exercise(
"Mod:Iface",
newCid,
"IfaceChoice3",
ValueUnit,
)
// TEST_EVIDENCE: Input Validation: well formed create-and-exercise API command is accepted // TEST_EVIDENCE: Input Validation: well formed create-and-exercise API command is accepted
val validCreateAndExe = ApiCommand.CreateAndExercise( val validCreateAndExe = ApiCommand.CreateAndExercise(
"Mod:Record", "Mod:Record",
@ -108,6 +156,8 @@ class ApiCommandPreprocessorSpec
validCreate, validCreate,
validExe, validExe,
validExeByKey, validExeByKey,
validExeByInterface,
validExeByRequiredInterface,
validCreateAndExe, validCreateAndExe,
) )
@ -125,6 +175,10 @@ class ApiCommandPreprocessorSpec
a[Error.Preprocessing.Lookup], a[Error.Preprocessing.Lookup],
validExe.copy(argument = ValueRecord("", ImmArray("content" -> ValueInt64(42)))) -> validExe.copy(argument = ValueRecord("", ImmArray("content" -> ValueInt64(42)))) ->
a[Error.Preprocessing.TypeMismatch], a[Error.Preprocessing.TypeMismatch],
// TEST_EVIDENCE: Input Validation: exercise-by-interface command is rejected for a
// choice of another interface.
validExeByInterface.copy(choiceId = "IfaceChoice2") ->
a[Error.Preprocessing.Lookup],
// TEST_EVIDENCE: Input Validation: ill-formed exercise-by-key API command is rejected // TEST_EVIDENCE: Input Validation: ill-formed exercise-by-key API command is rejected
validExeByKey.copy(templateId = "Mod:Undefined") -> validExeByKey.copy(templateId = "Mod:Undefined") ->
a[Error.Preprocessing.Lookup], a[Error.Preprocessing.Lookup],

View File

@ -58,6 +58,8 @@ class InterfacesTest
val lookupPackage = allInterfacesPkgs.get(_) val lookupPackage = allInterfacesPkgs.get(_)
val idI1 = Identifier(interfacesPkgId, "Interfaces:I1") val idI1 = Identifier(interfacesPkgId, "Interfaces:I1")
val idI2 = Identifier(interfacesPkgId, "Interfaces:I2") val idI2 = Identifier(interfacesPkgId, "Interfaces:I2")
val idI3 = Identifier(interfacesPkgId, "Interfaces:I3")
val idI4 = Identifier(interfacesPkgId, "Interfaces:I4")
val idT1 = Identifier(interfacesPkgId, "Interfaces:T1") val idT1 = Identifier(interfacesPkgId, "Interfaces:T1")
val idT2 = Identifier(interfacesPkgId, "Interfaces:T2") val idT2 = Identifier(interfacesPkgId, "Interfaces:T2")
val let = Time.Timestamp.now() val let = Time.Timestamp.now()
@ -128,6 +130,14 @@ class InterfacesTest
) )
} }
} }
"be unable to exercise an interface I4 choice via I3 on a T1 contract" in {
val command = ApiCommand.Exercise(idI3, cid2, "C4", ValueRecord(None, ImmArray.empty))
inside(runApi(command)) { case Left(Error.Interpretation(err, _)) =>
err shouldBe Error.Interpretation.DamlException(
IE.ContractDoesNotImplementRequiringInterface(idI3, idI4, cid2, idT2)
)
}
}
"be able to exercise T1 by interface I1" in { "be able to exercise T1 by interface I1" in {
val command = ApiCommand.Exercise(idT1, cid1, "C1", ValueRecord(None, ImmArray.empty)) val command = ApiCommand.Exercise(idT1, cid1, "C1", ValueRecord(None, ImmArray.empty))

View File

@ -46,6 +46,14 @@ private[lf] object Command {
argument: SValue, argument: SValue,
) extends Command ) extends Command
final case class ExerciseByInheritedInterface(
requiredIface: Identifier,
requiringIface: Identifier,
contractId: SContractId,
choiceId: ChoiceName,
argument: SValue,
) extends Command
final case class ExerciseByKey( final case class ExerciseByKey(
templateId: Identifier, templateId: Identifier,
contractKey: SValue, contractKey: SValue,

View File

@ -859,6 +859,26 @@ private[lf] final class Compiler(
) )
} }
private[this] def compileExerciseByInheritedInterface(
env: Env,
requiredIfaceId: TypeConName,
requiringIfaceId: TypeConName,
contractId: SValue,
choiceId: ChoiceName,
argument: SValue,
): s.SExpr =
unaryFunction(env) { (tokenPos, env) =>
t.GuardedChoiceDefRef(requiredIfaceId, choiceId)(
s.SEValue(contractId),
s.SEValue(argument),
s.SEApp(
s.SEBuiltin(SBGuardRequiredInterfaceId(requiredIfaceId, requiringIfaceId)),
List(s.SEValue(contractId)),
),
env.toSEVar(tokenPos),
)
}
private[this] def compileFetchByInterface( private[this] def compileFetchByInterface(
env: Env, env: Env,
interfaceId: TypeConName, interfaceId: TypeConName,
@ -880,6 +900,21 @@ private[lf] final class Compiler(
compileExerciseByInterface(env, interfaceId, templateId, contractId, choiceId, argument) compileExerciseByInterface(env, interfaceId, templateId, contractId, choiceId, argument)
case Command.ExerciseInterface(interfaceId, contractId, choiceId, argument) => case Command.ExerciseInterface(interfaceId, contractId, choiceId, argument) =>
t.ChoiceDefRef(interfaceId, choiceId)(s.SEValue(contractId), s.SEValue(argument)) t.ChoiceDefRef(interfaceId, choiceId)(s.SEValue(contractId), s.SEValue(argument))
case Command.ExerciseByInheritedInterface(
requiredIfaceId,
requiringIfaceId,
contractId,
choiceId,
argument,
) =>
compileExerciseByInheritedInterface(
env,
requiredIfaceId,
requiringIfaceId,
contractId,
choiceId,
argument,
)
case Command.ExerciseByKey(templateId, contractKey, choiceId, argument) => case Command.ExerciseByKey(templateId, contractKey, choiceId, argument) =>
t.ChoiceByKeyDefRef(templateId, choiceId)(s.SEValue(contractKey), s.SEValue(argument)) t.ChoiceByKeyDefRef(templateId, choiceId)(s.SEValue(contractKey), s.SEValue(argument))
case Command.Fetch(templateId, coid) => case Command.Fetch(templateId, coid) =>

View File

@ -349,10 +349,12 @@ private[lf] final class PhaseOne(
compileExp(env, exp) { exp => compileExp(env, exp) { exp =>
Return(SBFromRequiredInterface(requiringIfaceId)(exp)) Return(SBFromRequiredInterface(requiringIfaceId)(exp))
} }
case EUnsafeFromRequiredInterface(requiredIfaceId @ _, requiringIfaceId, cidExp, ifaceExp) => case EUnsafeFromRequiredInterface(requiredIfaceId, requiringIfaceId, cidExp, ifaceExp) =>
compileExp(env, cidExp) { cidExp => compileExp(env, cidExp) { cidExp =>
compileExp(env, ifaceExp) { ifaceExp => compileExp(env, ifaceExp) { ifaceExp =>
Return(SBUnsafeFromRequiredInterface(requiringIfaceId)(cidExp, ifaceExp)) Return(
SBUnsafeFromRequiredInterface(requiredIfaceId, requiringIfaceId)(cidExp, ifaceExp)
)
} }
} }
case EInterfaceTemplateTypeRep(ifaceId, exp) => case EInterfaceTemplateTypeRep(ifaceId, exp) =>

View File

@ -87,6 +87,18 @@ private[lf] object Pretty {
) / ) /
text("Expected contract to implement interface") & prettyTypeConName(interfaceId) & text("Expected contract to implement interface") & prettyTypeConName(interfaceId) &
text("but contract has type") & prettyTypeConName(templateId) text("but contract has type") & prettyTypeConName(templateId)
case ContractDoesNotImplementRequiringInterface(
requiringIfaceId,
requiredIfaceId,
coid,
templateId,
) =>
text("Update failed due to contract") & prettyContractId(coid) & text(
"not implementing the requiring interface"
) /
text("Expected contract to implement interface") & prettyTypeConName(requiringIfaceId) &
text("requirring the interface") & prettyTypeConName(requiredIfaceId) &
text("but contract has type") & prettyTypeConName(templateId)
case CreateEmptyContractKeyMaintainers(tid, arg, key) => case CreateEmptyContractKeyMaintainers(tid, arg, key) =>
text("Update failed due to a contract key with an empty sey of maintainers when creating") & text("Update failed due to a contract key with an empty sey of maintainers when creating") &
prettyTypeConName(tid) & text("with") & prettyValue(true)(arg) / prettyTypeConName(tid) & text("with") & prettyValue(true)(arg) /

View File

@ -1136,6 +1136,32 @@ private[lf] object SBuiltin {
} }
} }
final case class SBGuardRequiredInterfaceId(
requiredIfaceId: TypeConName,
requiringIfaceId: TypeConName,
) extends SBuiltin(2) {
override private[speedy] def execute(
args: util.ArrayList[SValue],
machine: Machine,
) = {
val contractId = getSContractId(args, 0)
val (actualTmplId, record @ _) = getSAnyContract(args, 1)
machine.returnValue = machine.compiledPackages.interface.lookupTemplate(actualTmplId) match {
case Right(ifaceSignature) if ifaceSignature.implements.contains(requiringIfaceId) =>
SBool(true)
case _ =>
throw SErrorDamlException(
IE.ContractDoesNotImplementRequiringInterface(
requiringIfaceId,
requiredIfaceId,
contractId,
actualTmplId,
)
)
}
}
}
final case class SBResolveSBUBeginExercise( final case class SBResolveSBUBeginExercise(
choiceName: ChoiceName, choiceName: ChoiceName,
consuming: Boolean, consuming: Boolean,
@ -1243,7 +1269,8 @@ private[lf] object SBuiltin {
// Convert an interface `requiredIface` to another interface `requiringIface`, if // Convert an interface `requiredIface` to another interface `requiringIface`, if
// the `requiringIface` implements `requiredIface`. // the `requiringIface` implements `requiredIface`.
final case class SBUnsafeFromRequiredInterface( final case class SBUnsafeFromRequiredInterface(
requiringIface: TypeConName requiredIfaceId: TypeConName,
requiringIfaceId: TypeConName,
) extends SBuiltin(2) { ) extends SBuiltin(2) {
override private[speedy] def execute( override private[speedy] def execute(
@ -1256,10 +1283,17 @@ private[lf] object SBuiltin {
// TODO https://github.com/digital-asset/daml/issues/11345 // TODO https://github.com/digital-asset/daml/issues/11345
// The lookup is probably slow. We may want to investigate way to make the feature faster. // The lookup is probably slow. We may want to investigate way to make the feature faster.
machine.returnValue = machine.compiledPackages.interface.lookupTemplate(tyCon) match { machine.returnValue = machine.compiledPackages.interface.lookupTemplate(tyCon) match {
case Right(ifaceSignature) if ifaceSignature.implements.contains(requiringIface) => case Right(ifaceSignature) if ifaceSignature.implements.contains(requiringIfaceId) =>
SAnyContract(tyCon, record) SAnyContract(tyCon, record)
case _ => case _ =>
throw SErrorDamlException(IE.WronglyTypedContract(coid, requiringIface, tyCon)) throw SErrorDamlException(
IE.ContractDoesNotImplementRequiringInterface(
requiringIfaceId,
requiredIfaceId,
coid,
tyCon,
)
)
} }
} }
} }

View File

@ -317,7 +317,19 @@ private[lf] class PackageInterface(signatures: PartialFunction[PackageId, Packag
case Right(interface) => case Right(interface) =>
interface.fixedChoices.get(chName) match { interface.fixedChoices.get(chName) match {
case Some(choice) => Right(ChoiceInfo.Interface(choice)) case Some(choice) => Right(ChoiceInfo.Interface(choice))
case None => Left(LookupError(context, context)) case None => {
// TODO(drsk) improve the performance of this lookup. Tracked in issue
// https://github.com/digital-asset/daml/issues/11345.
interface.requires.view
.map((iface) =>
lookupInterfaceChoice(iface, chName, context).map((choice) => (choice, iface))
)
.collectFirst({ case Right((choice, iface)) => (choice, iface) }) match {
case Some((choice, iface)) =>
Right(ChoiceInfo.InterfaceInherited(iface, choice))
case None => Left(LookupError(context, context))
}
}
} }
} }
} }
@ -449,6 +461,11 @@ object PackageInterface {
final case class Inherited(ifaceId: Identifier, choice: TemplateChoiceSignature) final case class Inherited(ifaceId: Identifier, choice: TemplateChoiceSignature)
extends ChoiceInfo extends ChoiceInfo
final case class InterfaceInherited(
ifaceId: Identifier,
choice: TemplateChoiceSignature,
) extends ChoiceInfo
} }
} }

View File

@ -15,6 +15,13 @@ interface I2 where
controller getOwner2 this controller getOwner2 this
do pure () do pure ()
interface I3 requires I4 where
interface I4 where
getOwner4 : Party
choice C4 : ()
controller getOwner4 this
do pure ()
template T1 template T1
with with
owner1 : Party owner1 : Party
@ -35,3 +42,5 @@ template T2
getOwner1 = owner2 getOwner1 = owner2
implements I2 where implements I2 where
getOwner2 = owner2 getOwner2 = owner2
implements I4 where
getOwner4 = owner2

View File

@ -98,6 +98,16 @@ object Error {
templateId: TypeConName, templateId: TypeConName,
) extends Error ) extends Error
/** We tried to exercise a contract by required interface, but
* the contract does not implement the requiring interface.
*/
final case class ContractDoesNotImplementRequiringInterface(
requiringInterfaceId: TypeConName,
requiredInterfaceId: TypeConName,
coid: ContractId,
templateId: TypeConName,
) extends Error
/** There was an authorization failure during execution. */ /** There was an authorization failure during execution. */
final case class FailedAuthorization( final case class FailedAuthorization(
nid: NodeId, nid: NodeId,

View File

@ -106,6 +106,11 @@ object RejectionGenerators {
.Error( .Error(
renderedMessage renderedMessage
) )
case _: LfInterpretationError.ContractDoesNotImplementRequiringInterface =>
LedgerApiErrors.CommandExecution.Interpreter.InvalidArgumentInterpretationError
.Error(
renderedMessage
)
case LfInterpretationError.NonComparableValues => case LfInterpretationError.NonComparableValues =>
LedgerApiErrors.CommandExecution.Interpreter.InvalidArgumentInterpretationError LedgerApiErrors.CommandExecution.Interpreter.InvalidArgumentInterpretationError
.Error( .Error(

View File

@ -212,15 +212,16 @@
- ensure expression forms have the correct type: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L107) - ensure expression forms have the correct type: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L107)
- error on specifying both authCommonUri and authInternalUri/authExternalUri for the trigger service: [AuthorizationConfigTest.scala](triggers/service/src/test-suite/scala/com/daml/lf/engine/trigger/AuthorizationConfigTest.scala#L24) - error on specifying both authCommonUri and authInternalUri/authExternalUri for the trigger service: [AuthorizationConfigTest.scala](triggers/service/src/test-suite/scala/com/daml/lf/engine/trigger/AuthorizationConfigTest.scala#L24)
- error on specifying only authInternalUri and no authExternalUri for the trigger service: [AuthorizationConfigTest.scala](triggers/service/src/test-suite/scala/com/daml/lf/engine/trigger/AuthorizationConfigTest.scala#L52) - error on specifying only authInternalUri and no authExternalUri for the trigger service: [AuthorizationConfigTest.scala](triggers/service/src/test-suite/scala/com/daml/lf/engine/trigger/AuthorizationConfigTest.scala#L52)
- exercise-by-interface command is rejected for a: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L178)
- give a 'not found' response for a stop request on an unknown UUID in the trigger service: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L515) - give a 'not found' response for a stop request on an unknown UUID in the trigger service: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L515)
- give a 'not found' response for a stop request with an unparseable UUID in the trigger service: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L500) - give a 'not found' response for a stop request with an unparseable UUID in the trigger service: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L500)
- ill-formed create API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L116) - ill-formed create API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L166)
- ill-formed create replay command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L108) - ill-formed create replay command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L108)
- ill-formed create-and-exercise API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L137) - ill-formed create-and-exercise API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L191)
- ill-formed exception definitions are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L1609) - ill-formed exception definitions are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L1609)
- ill-formed exercise API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L121) - ill-formed exercise API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L171)
- ill-formed exercise replay command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L113) - ill-formed exercise replay command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L113)
- ill-formed exercise-by-key API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L128) - ill-formed exercise-by-key API command is rejected: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L182)
- ill-formed exercise-by-key replay command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L120) - ill-formed exercise-by-key replay command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L120)
- ill-formed expressions are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L441) - ill-formed expressions are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L441)
- ill-formed fetch command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L167) - ill-formed fetch command is rejected: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L167)
@ -234,12 +235,14 @@
- ill-formed type synonyms definitions are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L1797) - ill-formed type synonyms definitions are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L1797)
- ill-formed types are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L99) - ill-formed types are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L99)
- ill-formed variants are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L1774) - ill-formed variants are rejected: [TypingSpec.scala](daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala#L1774)
- well formed create API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L80) - well formed create API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L113)
- well formed create replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L80) - well formed create replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L80)
- well formed create-and-exercise API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L99) - well formed create-and-exercise API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L147)
- well formed exercise API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L85) - well formed exercise API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L118)
- well formed exercise replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L85) - well formed exercise replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L85)
- well formed exercise-by-key API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L92) - well formed exercise-by-interface command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L132)
- well formed exercise-by-interface via required interface command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L140)
- well formed exercise-by-key API command is accepted: [ApiCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ApiCommandPreprocessorSpec.scala#L125)
- well formed exercise-by-key command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L92) - well formed exercise-by-key command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L92)
- well formed fetch replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L144) - well formed fetch replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L144)
- well formed fetch-by-key replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L149) - well formed fetch-by-key replay command is accepted: [ReplayCommandPreprocessorSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReplayCommandPreprocessorSpec.scala#L149)