[Trigger] update Created with interface (#15299)

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2022-10-24 10:00:01 +02:00 committed by GitHub
parent 1a4ef5b238
commit 15dd81b524
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 28 deletions

View File

@ -148,7 +148,8 @@ lookupTpl cid acs = do
-- | HIDE -- | HIDE
applyEvent : Event -> ACS -> ACS applyEvent : Event -> ACS -> ACS
applyEvent ev acs = case ev of applyEvent ev acs = case ev of
CreatedEvent (Created _ cid tpl) -> insertTpl cid tpl acs CreatedEvent (Created _ cid (Some tpl) _) -> insertTpl cid tpl acs
CreatedEvent _ -> acs
ArchivedEvent (Archived _ cid) -> deleteTpl cid acs ArchivedEvent (Archived _ cid) -> deleteTpl cid acs
-- | HIDE -- | HIDE

View File

@ -95,6 +95,15 @@ data Transaction = Transaction
, events : [Event] , events : [Event]
} }
-- TODO: https://github.com/digital-asset/daml/issues/14830
-- replace by DA.Internal.Any.AnyView.AnyView once it is introduced as stable package
type AnyView = ()
data InterfaceView = InterfaceView {
interfaceTypeRep : TemplateTypeRep,
anyView: Optional AnyView
}
-- | An event in a transaction. -- | An event in a transaction.
-- This definition should be kept consistent with the object `EventVariant` defined in -- This definition should be kept consistent with the object `EventVariant` defined in
-- triggers/runner/src/main/scala/com/digitalasset/daml/lf/engine/trigger/Converter.scala -- triggers/runner/src/main/scala/com/digitalasset/daml/lf/engine/trigger/Converter.scala
@ -106,15 +115,17 @@ data Event
data Created = Created data Created = Created
{ eventId : EventId { eventId : EventId
, contractId : AnyContractId , contractId : AnyContractId
, argument : AnyTemplate , argument : Optional AnyTemplate
, views : [InterfaceView]
} }
-- | Check if a `Created` event corresponds to the given template. -- | Check if a `Created` event corresponds to the given template.
fromCreated : Template t => Created -> Optional (EventId, ContractId t, t) fromCreated : Template t => Created -> Optional (EventId, ContractId t, t)
fromCreated Created {eventId, contractId, argument} fromCreated Created {eventId, contractId, argument}
| Some contractId' <- fromAnyContractId contractId | Some contractId' <- fromAnyContractId contractId
, Some argument' <- fromAnyTemplate argument , Some argument' <- argument
= Some (eventId, contractId', argument') , Some argument'' <- fromAnyTemplate argument'
= Some (eventId, contractId', argument'')
| otherwise | otherwise
= None = None

View File

@ -6,6 +6,8 @@ package engine
package trigger package trigger
import scalaz.std.either._ import scalaz.std.either._
import scalaz.std.option._
import scalaz.std.list._
import scalaz.syntax.traverse._ import scalaz.syntax.traverse._
import com.daml.lf.data.{FrontStack, ImmArray} import com.daml.lf.data.{FrontStack, ImmArray}
import com.daml.lf.data.Ref._ import com.daml.lf.data.Ref._
@ -21,7 +23,7 @@ import com.daml.ledger.api.v1.commands.{
ExerciseCommand, ExerciseCommand,
} }
import com.daml.ledger.api.v1.completion.Completion import com.daml.ledger.api.v1.completion.Completion
import com.daml.ledger.api.v1.event.{ArchivedEvent, CreatedEvent, Event} import com.daml.ledger.api.v1.event.{ArchivedEvent, CreatedEvent, Event, InterfaceView}
import com.daml.ledger.api.v1.transaction.Transaction import com.daml.ledger.api.v1.transaction.Transaction
import com.daml.ledger.api.v1.value import com.daml.ledger.api.v1.value
import com.daml.ledger.api.validation.NoLoggingValueValidator import com.daml.ledger.api.validation.NoLoggingValueValidator
@ -36,7 +38,10 @@ import com.daml.platform.participant.util.LfEngineToApi.{
import scala.concurrent.duration.{FiniteDuration, MICROSECONDS} import scala.concurrent.duration.{FiniteDuration, MICROSECONDS}
// Convert from a Ledger API transaction to an SValue corresponding to a Message from the Daml.Trigger module // Convert from a Ledger API transaction to an SValue corresponding to a Message from the Daml.Trigger module
final class Converter(compiledPackages: CompiledPackages, triggerIds: TriggerIds) { final class Converter(
compiledPackages: CompiledPackages,
triggerDef: TriggerDefinition,
) {
import Converter._ import Converter._
import com.daml.script.converter.Converter._, Implicits._ import com.daml.script.converter.Converter._, Implicits._
@ -52,8 +57,13 @@ final class Converter(compiledPackages: CompiledPackages, triggerIds: TriggerIds
private[this] def translateValue(ty: Type, value: Value): Either[String, SValue] = private[this] def translateValue(ty: Type, value: Value): Either[String, SValue] =
valueTranslator.translateValue(ty, value).left.map(res => s"Failure to translate value: $res") valueTranslator.translateValue(ty, value).left.map(res => s"Failure to translate value: $res")
private[this] val anyTemplateTyCon = DA.Internal.Any.assertIdentifier("AnyTemplate")
private[this] val templateTypeRepTyCon = DA.Internal.Any.assertIdentifier("TemplateTypeRep") private[this] val templateTypeRepTyCon = DA.Internal.Any.assertIdentifier("TemplateTypeRep")
private[this] val anyTemplateTyCon = DA.Internal.Any.assertIdentifier("AnyTemplate")
// TODO: https://github.com/digital-asset/daml/issues/14830
// replace by DA.Internal.Any.AnyView.AnyView once it is introduced as stable package
private[this] val anyViewTyCon = DA.Internal.Any.assertIdentifier("AnyView")
private[this] def triggerIds = triggerDef.triggerIds
private[this] val activeContractsTy = triggerIds.damlTriggerLowLevel("ActiveContracts") private[this] val activeContractsTy = triggerIds.damlTriggerLowLevel("ActiveContracts")
private[this] val anyContractIdTy = triggerIds.damlTriggerLowLevel("AnyContractId") private[this] val anyContractIdTy = triggerIds.damlTriggerLowLevel("AnyContractId")
@ -130,25 +140,66 @@ final class Converter(compiledPackages: CompiledPackages, triggerIds: TriggerIds
"contractId" -> fromAnyContractId(archived.getTemplateId, archived.contractId), "contractId" -> fromAnyContractId(archived.getTemplateId, archived.contractId),
) )
private[this] def fromCreatedEvent( private[this] def fromRecord(typ: Type, record: value.Record): Either[String, SValue] =
for {
record <- validateRecord(record)
tmplPayload <- translateValue(typ, record)
} yield tmplPayload
private[this] def fromAnyTemplate(typ: Type, value: SValue) =
record(anyTemplateTyCon, "getAnyTemplate" -> SAny(typ, value))
private[this] def fromV20CreatedEvent(
created: CreatedEvent created: CreatedEvent
): Either[String, SValue] = ): Either[String, SValue] =
for { for {
tmplId <- fromIdentifier(created.getTemplateId) tmplId <- fromIdentifier(created.getTemplateId)
createArguments <- validateRecord(created.getCreateArguments) tmplType = TTyCon(tmplId)
tmplPayload <- translateValue(TTyCon(tmplId), createArguments) tmplPayload <- fromRecord(tmplType, created.getCreateArguments)
} yield { } yield {
record( record(
createdTy, createdTy,
"eventId" -> fromEventId(created.eventId), "eventId" -> fromEventId(created.eventId),
"contractId" -> fromAnyContractId(created.getTemplateId, created.contractId), "contractId" -> fromAnyContractId(created.getTemplateId, created.contractId),
"argument" -> record( "argument" -> fromAnyTemplate(tmplType, tmplPayload),
anyTemplateTyCon,
"getAnyTemplate" -> SAny(TTyCon(tmplId), tmplPayload),
),
) )
} }
private[this] def fromAnyView(typ: Type, value: SValue) =
record(anyViewTyCon, "getAnyView" -> SAny(typ, value))
private[this] def fromInterfaceView(view: InterfaceView): Either[String, SOptional] =
for {
ifaceId <- fromIdentifier(view.getInterfaceId)
iface <- compiledPackages.pkgInterface.lookupInterface(ifaceId).left.map(_.pretty)
viewType = iface.view
viewValue <- view.viewValue.traverseU(fromRecord(viewType, _))
} yield SOptional(viewValue.map(fromAnyView(viewType, _)))
private[this] def fromV25CreatedEvent(
created: CreatedEvent
): Either[String, SValue] =
for {
tmplId <- fromIdentifier(created.getTemplateId)
tmplType = TTyCon(tmplId)
tmplPayload <- created.createArguments.traverseU(fromRecord(tmplType, _))
views <- created.interfaceViews.toList.traverseU(fromInterfaceView)
} yield {
record(
createdTy,
"eventId" -> fromEventId(created.eventId),
"contractId" -> fromAnyContractId(created.getTemplateId, created.contractId),
"argument" -> SOptional(tmplPayload.map(fromAnyTemplate(tmplType, _))),
"views" -> SList(views.to(FrontStack)),
)
}
private[this] val fromCreatedEvent: CreatedEvent => Either[String, SValue] =
triggerDef.version match {
case Trigger.Version.`2.0` => fromV20CreatedEvent
case Trigger.Version.`2.5` => fromV25CreatedEvent
}
private def fromEvent(ev: Event): Either[String, SValue] = private def fromEvent(ev: Event): Either[String, SValue] =
ev.event match { ev.event match {
case Event.Event.Archived(archivedEvent) => case Event.Event.Archived(archivedEvent) =>
@ -427,6 +478,7 @@ object Converter {
private final case class AnyContractId(templateId: Identifier, contractId: ContractId) private final case class AnyContractId(templateId: Identifier, contractId: ContractId)
private final case class AnyTemplate(ty: Identifier, arg: SValue) private final case class AnyTemplate(ty: Identifier, arg: SValue)
private final case class AnyChoice(name: ChoiceName, arg: SValue) private final case class AnyChoice(name: ChoiceName, arg: SValue)
private final case class AnyContractKey(key: SValue) private final case class AnyContractKey(key: SValue)

View File

@ -222,7 +222,7 @@ object Trigger extends StrictLogging {
for { for {
triggerDef <- detectTriggerDefinition(compiledPackages.pkgInterface, triggerId) triggerDef <- detectTriggerDefinition(compiledPackages.pkgInterface, triggerId)
hasReadAs <- detectHasReadAs(compiledPackages.pkgInterface, triggerDef.triggerIds) hasReadAs <- detectHasReadAs(compiledPackages.pkgInterface, triggerDef.triggerIds)
converter = new Converter(compiledPackages, triggerDef.triggerIds) converter = new Converter(compiledPackages, triggerDef)
filter <- getTriggerFilter(compiledPackages, compiler, converter, triggerDef) filter <- getTriggerFilter(compiledPackages, compiler, converter, triggerDef)
heartbeat <- getTriggerHeartbeat(compiledPackages, compiler, converter, triggerDef) heartbeat <- getTriggerHeartbeat(compiledPackages, compiler, converter, triggerDef)
} yield Trigger(triggerDef, filter, heartbeat, hasReadAs) } yield Trigger(triggerDef, filter, heartbeat, hasReadAs)
@ -354,14 +354,14 @@ class Runner(
import Runner.{SeenMsgs, alterF} import Runner.{SeenMsgs, alterF}
// Compiles LF expressions into Speedy expressions. // Compiles LF expressions into Speedy expressions.
private val compiler: Compiler = compiledPackages.compiler private val compiler = compiledPackages.compiler
// Converts between various objects and SValues. // Converts between various objects and SValues.
private val converter: Converter = new Converter(compiledPackages, trigger.defn.triggerIds) private val converter: Converter = new Converter(compiledPackages, trigger.defn)
// These are the command IDs used on the ledger API to submit commands for // These are the command IDs used on the ledger API to submit commands for
// this trigger for which we are awaiting either a completion or transaction // this trigger for which we are awaiting either a completion or transaction
// message, or both. // message, or both.
private[this] var pendingCommandIds: Map[UUID, SeenMsgs] = Map.empty private[this] var pendingCommandIds = Map.empty[UUID, SeenMsgs]
private val transactionFilter: TransactionFilter = private val transactionFilter =
TransactionFilter(parties.readers.map(p => (p.unwrap, trigger.filters)).toMap) TransactionFilter(parties.readers.map(p => (p.unwrap, trigger.filters)).toMap)
private[this] def logger = ContextualizedLogger get getClass private[this] def logger = ContextualizedLogger get getClass

View File

@ -154,7 +154,6 @@ da_scala_library(
"//daml-lf/engine", "//daml-lf/engine",
"//daml-lf/interpreter", "//daml-lf/interpreter",
"//daml-lf/language", "//daml-lf/language",
"//daml-script/converter",
"//language-support/scala/bindings", "//language-support/scala/bindings",
"//language-support/scala/bindings-akka", "//language-support/scala/bindings-akka",
"//ledger-api/rs-grpc-bridge", "//ledger-api/rs-grpc-bridge",

View File

@ -13,7 +13,6 @@ import org.scalatest._
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AsyncWordSpec import org.scalatest.wordspec.AsyncWordSpec
import com.daml.lf.engine.trigger.TriggerMsg import com.daml.lf.engine.trigger.TriggerMsg
import com.daml.script.converter.ConverterException
class DevOnly class DevOnly
extends AsyncWordSpec extends AsyncWordSpec
@ -107,10 +106,9 @@ class DevOnly
} }
} }
// TODO: modify Converter so that it may parse interface and view related transaction messages "succeed with interface registration and implementing template not registered" in {
"fail with interface registration and implementing template not registered" in {
val triggerId = QualifiedName.assertFromString("InterfaceTriggers:triggerWithRegistration") val triggerId = QualifiedName.assertFromString("InterfaceTriggers:triggerWithRegistration")
val result = for { for {
client <- ledgerClient() client <- ledgerClient()
party <- allocateParty(client) party <- allocateParty(client)
runner = getRunner(client, triggerId, party) runner = getRunner(client, triggerId, party)
@ -163,10 +161,10 @@ class DevOnly
// 1 for create of template A // 1 for create of template A
// 1 for create of template B, via interface I // 1 for create of template B, via interface I
_ <- runner.runWithACS(acs, offset, msgFlow = Flow[TriggerMsg].take(2))._2 _ <- runner.runWithACS(acs, offset, msgFlow = Flow[TriggerMsg].take(2))._2
} yield fail() acs <- queryACS(client, party)
} yield {
result.recoverWith { case exn: ConverterException => acs(templateA) should have length 1
exn.getMessage should startWith("Failure to translate value: TypeMismatch") acs(templateB) should have length 1
} }
} }
} }