Skip to content

Commit

Permalink
Add separate commands for create/fetch/exercise by template/interface. (
Browse files Browse the repository at this point in the history
#11724)

* Add separate commands wrt by template/interface

Resolves #11674 and #11675

changelog_begin
changelog_end

* remove unnecessary commands
  • Loading branch information
sofiafaro-da authored Nov 17, 2021
1 parent 7e4acf9 commit c2d4ea4
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,71 @@ 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,
choiceId: ChoiceName,
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,
choiceId: ChoiceName,
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand All @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading

0 comments on commit c2d4ea4

Please sign in to comment.