mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
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:
parent
4a3d0b316b
commit
fcd3b6622b
@ -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 =
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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, _),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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],
|
||||||
|
@ -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))
|
||||||
|
@ -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,
|
||||||
|
@ -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) =>
|
||||||
|
@ -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) =>
|
||||||
|
@ -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) /
|
||||||
|
@ -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,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user