Add separate commands for create/fetch/exercise by template/interface. (#11724)

* Add separate commands wrt by template/interface

Resolves #11674 and #11675

changelog_begin
changelog_end

* remove unnecessary commands
This commit is contained in:
Sofia Faro 2021-11-17 17:08:24 +00:00 committed by GitHub
parent 7e4acf97fc
commit c2d4ea4ef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 246 additions and 17 deletions

View File

@ -33,34 +33,82 @@ private[lf] final class CommandPreprocessor(
templateId: Ref.Identifier,
argument: Value,
): speedy.Command.Create = {
discard(handleLookup(interface.lookupTemplate(templateId)))
val arg = valueTranslator.unsafeTranslateValue(Ast.TTyCon(templateId), argument)
speedy.Command.Create(templateId, arg)
}
@throws[Error.Preprocessing.Error]
def unsafePreprocessCreateByInterface(
interfaceId: Ref.Identifier,
templateId: Ref.Identifier,
argument: Value,
): speedy.Command.CreateByInterface = {
discard(handleLookup(interface.lookupTemplateImplements(templateId, interfaceId)))
val arg = valueTranslator.unsafeTranslateValue(Ast.TTyCon(templateId), argument)
speedy.Command.CreateByInterface(interfaceId, templateId, arg)
}
def unsafePreprocessExercise(
identifier: Ref.Identifier,
contractId: Value.ContractId,
choiceId: Ref.ChoiceName,
argument: Value,
): speedy.Command.Exercise = {
): speedy.Command = {
import language.PackageInterface.ChoiceInfo
val cid = valueTranslator.unsafeTranslateCid(contractId)
def command(id: Ref.Identifier, choice: Ast.TemplateChoiceSignature) = {
def command(
choice: Ast.TemplateChoiceSignature,
toSpeedyCommand: speedy.SValue => speedy.Command,
) = {
val arg = valueTranslator.unsafeTranslateValue(choice.argBinder._2, argument)
speedy.Command.Exercise(id, cid, choiceId, arg)
toSpeedyCommand(arg)
}
handleLookup(interface.lookupChoice(identifier, choiceId)) match {
case ChoiceInfo.Template(choice) =>
command(identifier, choice)
command(choice, speedy.Command.Exercise(identifier, cid, choiceId, _))
case ChoiceInfo.Interface(choice) =>
command(identifier, choice)
command(choice, speedy.Command.ExerciseInterface(identifier, cid, choiceId, _))
case ChoiceInfo.Inherited(ifaceId, choice) =>
command(ifaceId, choice)
command(choice, speedy.Command.ExerciseByInterface(ifaceId, identifier, cid, choiceId, _))
}
}
/* Like unsafePreprocessExercise, but expects the choice to come from the template specifically, not inherited from an interface. */
@throws[Error.Preprocessing.Error]
def unsafePreprocessExerciseTemplate(
templateId: Ref.Identifier,
contractId: Value.ContractId,
choiceId: Ref.ChoiceName,
argument: Value,
): speedy.Command.Exercise = {
val cid = valueTranslator.unsafeTranslateCid(contractId)
val choiceArgType = handleLookup(
interface.lookupTemplateChoice(templateId, choiceId)
).argBinder._2
val arg = valueTranslator.unsafeTranslateValue(choiceArgType, argument)
speedy.Command.Exercise(templateId, cid, choiceId, arg)
}
/* Like unsafePreprocessExercise, but expects the choice to be inherited from the given interface. */
@throws[Error.Preprocessing.Error]
def unsafePreprocessExerciseByInterface(
interfaceId: Ref.Identifier,
templateId: Ref.Identifier,
contractId: Value.ContractId,
choiceId: Ref.ChoiceName,
argument: Value,
): speedy.Command.ExerciseByInterface = {
val cid = valueTranslator.unsafeTranslateCid(contractId)
val choiceArgType = handleLookup(
interface.lookupInheritedChoice(interfaceId, templateId, choiceId)
).argBinder._2
val arg = valueTranslator.unsafeTranslateValue(choiceArgType, argument)
speedy.Command.ExerciseByInterface(interfaceId, templateId, cid, choiceId, arg)
}
@throws[Error.Preprocessing.Error]
def unsafePreprocessExerciseByKey(
templateId: Ref.Identifier,
@ -118,8 +166,20 @@ private[lf] final class CommandPreprocessor(
cmd match {
case command.CreateCommand(templateId, argument) =>
unsafePreprocessCreate(templateId, argument)
case command.CreateByInterfaceCommand(interfaceId, templateId, argument) =>
unsafePreprocessCreateByInterface(interfaceId, templateId, argument)
case command.ExerciseCommand(templateId, contractId, choiceId, argument) =>
unsafePreprocessExercise(templateId, contractId, choiceId, argument)
case command.ExerciseTemplateCommand(templateId, contractId, choiceId, argument) =>
unsafePreprocessExerciseTemplate(templateId, contractId, choiceId, argument)
case command.ExerciseByInterfaceCommand(
interfaceId,
templateId,
contractId,
choiceId,
argument,
) =>
unsafePreprocessExerciseByInterface(interfaceId, templateId, contractId, choiceId, argument)
case command.ExerciseByKeyCommand(templateId, contractKey, choiceId, argument) =>
unsafePreprocessExerciseByKey(templateId, contractKey, choiceId, argument)
case command.CreateAndExerciseCommand(
@ -134,12 +194,16 @@ private[lf] final class CommandPreprocessor(
choiceId,
choiceArgument,
)
case command.FetchCommand(templateId, coid) =>
// TODO https://github.com/digital-asset/daml/issues/10810
// -- handle the case where templateId is an interface
discard[Ast.TemplateSignature](handleLookup(interface.lookupTemplate(templateId)))
case command.FetchCommand(templateId, coid) => {
discard(handleLookup(interface.lookupTemplate(templateId)))
val cid = valueTranslator.unsafeTranslateCid(coid)
speedy.Command.Fetch(templateId, cid)
}
case command.FetchByInterfaceCommand(interfaceId, templateId, coid) => {
discard(handleLookup(interface.lookupTemplateImplements(templateId, interfaceId)))
val cid = valueTranslator.unsafeTranslateCid(coid)
speedy.Command.FetchByInterface(interfaceId, templateId, cid)
}
case command.FetchByKeyCommand(templateId, key) =>
val ckTtype = handleLookup(interface.lookupTemplateKey(templateId)).typ
val sKey = valueTranslator.unsafeTranslateValue(ckTtype, key)

View File

@ -71,7 +71,16 @@ private[preprocessing] final class TransactionPreprocessor(
case Some(node: Node.Action) =>
node match {
case create: Node.Create =>
val cmd = commandPreprocessor.unsafePreprocessCreate(create.templateId, create.arg)
val cmd = create.byInterface match {
case None =>
commandPreprocessor.unsafePreprocessCreate(create.templateId, create.arg)
case Some(interfaceId) =>
commandPreprocessor.unsafePreprocessCreateByInterface(
interfaceId,
create.templateId,
create.arg,
)
}
acc :+ cmd
case exe: Node.Exercise =>
val cmd = exe.key match {
@ -83,12 +92,23 @@ private[preprocessing] final class TransactionPreprocessor(
exe.chosenValue,
)
case _ =>
commandPreprocessor.unsafePreprocessExercise(
exe.templateId,
exe.targetCoid,
exe.choiceId,
exe.chosenValue,
)
exe.byInterface match {
case None =>
commandPreprocessor.unsafePreprocessExerciseTemplate(
exe.templateId,
exe.targetCoid,
exe.choiceId,
exe.chosenValue,
)
case Some(interfaceId) =>
commandPreprocessor.unsafePreprocessExerciseByInterface(
interfaceId,
exe.templateId,
exe.targetCoid,
exe.choiceId,
exe.chosenValue,
)
}
}
acc :+ cmd
case _: Node.Fetch =>

View File

@ -15,11 +15,20 @@ sealed abstract class Command extends Product with Serializable {
object Command {
/** Create a template, not by interface */
final case class Create(
templateId: Identifier,
argument: SValue,
) extends Command
/** Create a template, by interface */
final case class CreateByInterface(
interfaceId: Identifier,
templateId: Identifier,
argument: SValue,
) extends Command
/** Exercise a template choice, not by interface */
final case class Exercise(
templateId: Identifier,
contractId: SContractId,
@ -27,6 +36,30 @@ object Command {
argument: SValue,
) extends Command
/** Exercise a template choice, by interface */
final case class ExerciseByInterface(
interfaceId: Identifier,
templateId: Identifier,
contractId: SContractId,
choiceId: ChoiceName,
argument: SValue,
) extends Command
/** Exercise an interface choice. This is used for exercising an interface
* on the ledger api, where the template id is unknown.
*/
final case class ExerciseInterface(
interfaceId: Identifier,
contractId: SContractId,
choiceId: ChoiceName,
argument: SValue,
) extends Command {
// TODO https://github.com/digital-asset/daml/issues/11342
// The actual template id isn't known until run time.
// The interface id is the best we've got.
val templateId = interfaceId
}
final case class ExerciseByKey(
templateId: Identifier,
contractKey: SValue,
@ -34,11 +67,19 @@ object Command {
argument: SValue,
) extends Command
/** Fetch a template, not by interface */
final case class Fetch(
templateId: Identifier,
coid: SContractId,
) extends Command
/** Fetch a template, by interface */
final case class FetchByInterface(
interfaceId: Identifier,
templateId: Identifier,
coid: SContractId,
) extends Command
final case class FetchByKey(
templateId: Identifier,
key: SValue,

View File

@ -1411,12 +1411,24 @@ private[lf] final class Compiler(
private[this] def compileCommand(cmd: Command): s.SExpr = cmd match {
case Command.Create(templateId, argument) =>
t.CreateDefRef(templateId)(s.SEValue(argument))
case Command.CreateByInterface(interfaceId, templateId, argument) =>
t.CreateByInterfaceDefRef(templateId, interfaceId)(s.SEValue(argument))
case Command.Exercise(templateId, contractId, choiceId, argument) =>
t.ChoiceDefRef(templateId, choiceId)(s.SEValue(contractId), s.SEValue(argument))
case Command.ExerciseByInterface(interfaceId, templateId @ _, contractId, choiceId, argument) =>
// TODO https://github.com/digital-asset/daml/issues/11703
// Ensure that fetched template has expected templateId.
t.ChoiceDefRef(interfaceId, choiceId)(s.SEValue(contractId), s.SEValue(argument))
case Command.ExerciseInterface(interfaceId, contractId, choiceId, argument) =>
t.ChoiceDefRef(interfaceId, choiceId)(s.SEValue(contractId), s.SEValue(argument))
case Command.ExerciseByKey(templateId, contractKey, choiceId, argument) =>
t.ChoiceByKeyDefRef(templateId, choiceId)(s.SEValue(contractKey), s.SEValue(argument))
case Command.Fetch(templateId, coid) =>
t.FetchDefRef(templateId)(s.SEValue(coid))
case Command.FetchByInterface(interfaceId, templateId @ _, coid) =>
// TODO https://github.com/digital-asset/daml/issues/11703
// Ensure that fetched template has expected templateId.
t.FetchDefRef(interfaceId)(s.SEValue(coid))
case Command.FetchByKey(templateId, key) =>
t.FetchByKeyDefRef(templateId)(s.SEValue(key))
case Command.CreateAndExercise(templateId, createArg, choice, choiceArg) =>

View File

@ -103,6 +103,11 @@ object Reference {
override def pretty: String = s"template without contract key $tyCon."
}
final case class TemplateImplements(templateName: TypeConName, ifaceName: TypeConName)
extends Reference {
override def pretty: String = s"template $templateName implementation of interface $ifaceName"
}
final case class TemplateChoice(tyCon: TypeConName, choiceName: ChoiceName) extends Reference {
override def pretty: String = s"choice $choiceName in template $tyCon"
}
@ -111,6 +116,15 @@ object Reference {
override def pretty: String = s"choice $choiceName in interface $tyCon"
}
final case class InheritedChoice(
ifaceName: TypeConName,
templateName: TypeConName,
choiceName: ChoiceName,
) extends Reference {
override def pretty: String =
s"choice $choiceName in template $templateName by interface $ifaceName"
}
final case class TemplateOrInterface(tyCon: TypeConName) extends Reference {
override def pretty: String = s"template or interface $tyCon"
}

View File

@ -219,6 +219,23 @@ private[lf] class PackageInterface(signatures: PartialFunction[PackageId, Packag
): Either[LookupError, TemplateChoiceSignature] =
lookupTemplateChoice(tmpName, chName, Reference.TemplateChoice(tmpName, chName))
private[this] def lookupTemplateImplements(
tmpName: TypeConName,
ifaceName: TypeConName,
context: => Reference,
): Either[LookupError, TemplateImplementsSignature] =
lookupTemplate(tmpName, context).flatMap(
_.implements
.get(ifaceName)
.toRight(LookupError(Reference.TemplateImplements(tmpName, ifaceName), context))
)
def lookupTemplateImplements(
tmpName: TypeConName,
ifaceName: TypeConName,
): Either[LookupError, TemplateImplementsSignature] =
lookupTemplateImplements(tmpName, ifaceName, Reference.TemplateImplements(tmpName, ifaceName))
private[this] def lookupInterfaceChoice(
ifaceName: TypeConName,
chName: ChoiceName,
@ -236,6 +253,35 @@ private[lf] class PackageInterface(signatures: PartialFunction[PackageId, Packag
): Either[LookupError, TemplateChoiceSignature] =
lookupInterfaceChoice(ifaceName, chName, Reference.InterfaceChoice(ifaceName, chName))
/* Looks up a choice inherited by a template through a specific interface.
* Fails if the template does not implement the interface, and/or the choice is not defined in this interface. */
private[lf] def lookupInheritedChoice(
ifaceName: TypeConName,
tmpName: TypeConName,
chName: ChoiceName,
context: => Reference,
): Either[LookupError, TemplateChoiceSignature] =
lookupTemplate(tmpName, context).flatMap(template =>
template.inheritedChoices.get(chName) match {
case Some(gotIfaceName) if gotIfaceName == ifaceName =>
lookupInterfaceChoice(ifaceName, chName, context)
case _ =>
Left(LookupError(Reference.InheritedChoice(ifaceName, tmpName, chName), context))
}
)
private[lf] def lookupInheritedChoice(
ifaceName: TypeConName,
tmpName: TypeConName,
chName: ChoiceName,
): Either[LookupError, TemplateChoiceSignature] =
lookupInheritedChoice(
ifaceName,
tmpName,
chName,
Reference.InheritedChoice(ifaceName, tmpName, chName),
)
private[lf] def lookupTemplateOrInterface(
identier: TypeConName,
context: => Reference,

View File

@ -27,6 +27,13 @@ sealed abstract class ApiCommand extends Command
*/
final case class CreateCommand(templateId: Identifier, argument: Value) extends ApiCommand
/** Create template contract, by interface */
final case class CreateByInterfaceCommand(
interfaceId: Identifier,
templateId: Identifier,
argument: Value,
) extends Command
/** Command for exercising a choice on an existing contract
*
* @param templateId identifier of the original contract
@ -41,6 +48,23 @@ final case class ExerciseCommand(
argument: Value,
) extends ApiCommand
/** Exercise a template choice, not by interface. */
final case class ExerciseTemplateCommand(
templateId: Identifier,
contractId: Value.ContractId,
choiceId: ChoiceName,
argument: Value,
) extends Command
/** Exercise a template choice, by interface. */
final case class ExerciseByInterfaceCommand(
interfaceId: Identifier,
templateId: Identifier,
contractId: Value.ContractId,
choiceId: ChoiceName,
argument: Value,
) extends Command
/** Command for exercising a choice on an existing contract specified by its key
*
* @param templateId identifier of the original contract
@ -70,11 +94,19 @@ final case class CreateAndExerciseCommand(
choiceArgument: Value,
) extends ApiCommand
/** Fetch a template, not by interface */
final case class FetchCommand(
templateId: Identifier,
coid: Value.ContractId,
) extends Command
/** Fetch a template, by interface */
final case class FetchByInterfaceCommand(
interfaceId: Identifier,
templateId: Identifier,
coid: Value.ContractId,
) extends Command
final case class FetchByKeyCommand(
templateId: Identifier,
key: Value,