Conformance Tests for Interface Subscriptions [DPP-1159] (#14750)

* Conformance Tests for Interface Subscriptions [DPP-1159]

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Sergey Kisel 2022-08-22 08:01:47 +02:00 committed by GitHub
parent 24268b25ae
commit 1f7dd3c018
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 845 additions and 84 deletions

View File

@ -4,10 +4,12 @@
package com.daml.ledger.api.testtool.infrastructure.participant
import java.time.Instant
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
import com.daml.ledger.api.testtool.infrastructure.Endpoint
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.CompletionResponse
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.{
CompletionResponse,
IncludeInterfaceView,
}
import com.daml.ledger.api.testtool.infrastructure.time.DelayMechanism
import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsRequest
import com.daml.ledger.api.v1.admin.config_management_service.{
@ -43,13 +45,14 @@ import com.daml.ledger.api.v1.ledger_configuration_service.LedgerConfiguration
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
import com.daml.ledger.api.v1.package_service.{GetPackageResponse, PackageStatus}
import com.daml.ledger.api.v1.transaction.{Transaction, TransactionTree}
import com.daml.ledger.api.v1.transaction_filter.{Filters, TransactionFilter}
import com.daml.ledger.api.v1.transaction_service.{
GetTransactionByEventIdRequest,
GetTransactionByIdRequest,
GetTransactionsRequest,
GetTransactionsResponse,
}
import com.daml.ledger.api.v1.value.{Identifier, Value}
import com.daml.ledger.api.v1.value.Value
import com.daml.ledger.client.binding.{Primitive, Template}
import com.daml.lf.data.Ref.HexString
import com.google.protobuf.ByteString
@ -139,24 +142,36 @@ trait ParticipantTestContext extends UserManagementTestContext {
): Future[(Option[LedgerOffset], Vector[CreatedEvent])]
def activeContractsRequest(
parties: Seq[Primitive.Party],
templateIds: Seq[Identifier] = Seq.empty,
templateIds: Seq[TemplateId] = Seq.empty,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): GetActiveContractsRequest
def activeContracts(parties: Primitive.Party*): Future[Vector[CreatedEvent]]
def activeContractsByTemplateId(
templateIds: Seq[Identifier],
templateIds: Seq[TemplateId],
parties: Primitive.Party*
): Future[Vector[CreatedEvent]]
/** Create a [[GetTransactionsRequest]] with a set of [[Party]] objects.
/** Create a [[TransactionFilter]] with a set of [[Party]] objects.
* You should use this only when you need to tweak the request of [[flatTransactions]]
* or [[transactionTrees]], otherwise use the shortcut override that allows you to
* directly pass a set of [[Party]]
*/
def getTransactionsRequest(
def transactionFilter(
parties: Seq[Primitive.Party],
templateIds: Seq[TemplateId] = Seq.empty,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): TransactionFilter
def filters(
templateIds: Seq[TemplateId] = Seq.empty,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): Filters
def getTransactionsRequest(
transactionFilter: TransactionFilter,
begin: LedgerOffset = referenceOffset,
): GetTransactionsRequest
def transactionStream(
request: GetTransactionsRequest,
responseObserver: StreamObserver[GetTransactionsResponse],
@ -377,6 +392,7 @@ trait ParticipantTestContext extends UserManagementTestContext {
}
object ParticipantTestContext {
type IncludeInterfaceView = Boolean
case class CompletionResponse(completion: Completion, offset: LedgerOffset, recordTime: Instant)

View File

@ -4,11 +4,13 @@
package com.daml.ledger.api.testtool.infrastructure.participant
import java.time.{Clock, Instant}
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
import com.daml.ledger.api.testtool.infrastructure.Eventually.eventually
import com.daml.ledger.api.testtool.infrastructure.ProtobufConverters._
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.CompletionResponse
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.{
CompletionResponse,
IncludeInterfaceView,
}
import com.daml.ledger.api.testtool.infrastructure.time.{
DelayMechanism,
StaticTimeDelayMechanism,
@ -71,7 +73,12 @@ import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
import com.daml.ledger.api.v1.package_service._
import com.daml.ledger.api.v1.testing.time_service.{GetTimeRequest, GetTimeResponse, SetTimeRequest}
import com.daml.ledger.api.v1.transaction.{Transaction, TransactionTree}
import com.daml.ledger.api.v1.transaction_filter.{Filters, InclusiveFilters, TransactionFilter}
import com.daml.ledger.api.v1.transaction_filter.{
Filters,
InclusiveFilters,
InterfaceFilter,
TransactionFilter,
}
import com.daml.ledger.api.v1.transaction_service.{
GetLedgerEndRequest,
GetTransactionByEventIdRequest,
@ -79,7 +86,7 @@ import com.daml.ledger.api.v1.transaction_service.{
GetTransactionsRequest,
GetTransactionsResponse,
}
import com.daml.ledger.api.v1.value.{Identifier, Value}
import com.daml.ledger.api.v1.value.Value
import com.daml.ledger.client.binding.Primitive.Party
import com.daml.ledger.client.binding.{Primitive, Template}
import com.daml.lf.data.Ref
@ -116,8 +123,6 @@ final class SingleParticipantTestContext private[participant] (
extends ParticipantTestContext {
private val logger = ContextualizedLogger.get(getClass)
import SingleParticipantTestContext._
private[this] val identifierPrefix =
s"$applicationId-$endpointId-$identifierSuffix"
@ -274,11 +279,12 @@ final class SingleParticipantTestContext private[participant] (
override def activeContractsRequest(
parties: Seq[Party],
templateIds: Seq[Identifier] = Seq.empty,
templateIds: Seq[TemplateId] = Seq.empty,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): GetActiveContractsRequest =
new GetActiveContractsRequest(
ledgerId = ledgerId,
filter = transactionFilter(Tag.unsubst(parties), templateIds),
filter = Some(transactionFilter(parties, templateIds, interfaceFilters)),
verbose = true,
)
@ -286,24 +292,50 @@ final class SingleParticipantTestContext private[participant] (
activeContractsByTemplateId(Seq.empty, parties: _*)
override def activeContractsByTemplateId(
templateIds: Seq[Identifier],
templateIds: Seq[TemplateId],
parties: Party*
): Future[Vector[CreatedEvent]] =
activeContracts(activeContractsRequest(parties, templateIds)).map(_._2)
override def getTransactionsRequest(
def transactionFilter(
parties: Seq[Party],
templateIds: Seq[TemplateId] = Seq.empty,
begin: LedgerOffset = referenceOffset,
): GetTransactionsRequest =
new GetTransactionsRequest(
ledgerId = ledgerId,
begin = Some(begin),
end = Some(end),
filter = transactionFilter(Tag.unsubst(parties), Tag.unsubst(templateIds)),
verbose = true,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): TransactionFilter =
new TransactionFilter(
parties.map(party => party.unwrap -> filters(templateIds, interfaceFilters)).toMap
)
def filters(
templateIds: Seq[TemplateId] = Seq.empty,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): Filters = new Filters(
if (templateIds.isEmpty && interfaceFilters.isEmpty) None
else
Some(
new InclusiveFilters(
templateIds = templateIds.map(Tag.unwrap).toSeq,
interfaceFilters = interfaceFilters.map { case (id, includeInterfaceView) =>
new InterfaceFilter(
Some(Tag.unwrap(id)),
includeInterfaceView = includeInterfaceView,
)
}.toSeq,
)
)
)
def getTransactionsRequest(
transactionFilter: TransactionFilter,
begin: LedgerOffset = referenceOffset,
): GetTransactionsRequest = new GetTransactionsRequest(
ledgerId = ledgerId,
begin = Some(begin),
end = Some(end),
filter = Some(transactionFilter),
verbose = true,
)
private def transactions[Res](
n: Int,
request: GetTransactionsRequest,
@ -327,14 +359,14 @@ final class SingleParticipantTestContext private[participant] (
templateId: TemplateId,
parties: Party*
): Future[Vector[Transaction]] =
flatTransactions(getTransactionsRequest(parties, Seq(templateId)))
flatTransactions(getTransactionsRequest(transactionFilter(parties, Seq(templateId))))
override def flatTransactions(request: GetTransactionsRequest): Future[Vector[Transaction]] =
transactions(request, services.transaction.getTransactions)
.map(_.flatMap(_.transactions))
override def flatTransactions(parties: Party*): Future[Vector[Transaction]] =
flatTransactions(getTransactionsRequest(parties))
flatTransactions(getTransactionsRequest(transactionFilter(parties)))
override def flatTransactions(
take: Int,
@ -344,20 +376,20 @@ final class SingleParticipantTestContext private[participant] (
.map(_.flatMap(_.transactions))
override def flatTransactions(take: Int, parties: Party*): Future[Vector[Transaction]] =
flatTransactions(take, getTransactionsRequest(parties))
flatTransactions(take, getTransactionsRequest(transactionFilter(parties)))
override def transactionTreesByTemplateId(
templateId: TemplateId,
parties: Party*
): Future[Vector[TransactionTree]] =
transactionTrees(getTransactionsRequest(parties, Seq(templateId)))
transactionTrees(getTransactionsRequest(transactionFilter(parties, Seq(templateId))))
override def transactionTrees(request: GetTransactionsRequest): Future[Vector[TransactionTree]] =
transactions(request, services.transaction.getTransactionTrees)
.map(_.flatMap(_.transactions))
override def transactionTrees(parties: Party*): Future[Vector[TransactionTree]] =
transactionTrees(getTransactionsRequest(parties))
transactionTrees(getTransactionsRequest(transactionFilter(parties)))
override def transactionTrees(
take: Int,
@ -367,7 +399,7 @@ final class SingleParticipantTestContext private[participant] (
.map(_.flatMap(_.transactions))
override def transactionTrees(take: Int, parties: Party*): Future[Vector[TransactionTree]] =
transactionTrees(take, getTransactionsRequest(parties))
transactionTrees(take, getTransactionsRequest(transactionFilter(parties)))
override def getTransactionByIdRequest(
transactionId: String,
@ -752,19 +784,3 @@ final class SingleParticipantTestContext private[participant] (
private def reservePartyNames(n: Int): Future[Vector[Party]] =
Future.successful(Vector.fill(n)(Party(nextPartyHintId())))
}
private[testtool] object SingleParticipantTestContext {
private[this] def filter(templateIds: Seq[Identifier]): Filters =
new Filters(
if (templateIds.isEmpty) None
else Some(new InclusiveFilters(templateIds))
)
private def transactionFilter(
parties: Seq[String],
templateIds: Seq[Identifier],
): Some[TransactionFilter] =
Some(new TransactionFilter(Map(parties.map(_ -> filter(templateIds)): _*)))
}

View File

@ -5,9 +5,9 @@ package com.daml.ledger.api.testtool.infrastructure.participant
import java.time.Instant
import java.util.concurrent.TimeoutException
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
import com.daml.ledger.api.testtool.infrastructure.Endpoint
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.IncludeInterfaceView
import com.daml.ledger.api.testtool.infrastructure.time.{DelayMechanism, Durations}
import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsRequest
import com.daml.ledger.api.v1.admin.config_management_service.{
@ -43,13 +43,14 @@ import com.daml.ledger.api.v1.ledger_configuration_service.LedgerConfiguration
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
import com.daml.ledger.api.v1.package_service.{GetPackageResponse, PackageStatus}
import com.daml.ledger.api.v1.transaction.{Transaction, TransactionTree}
import com.daml.ledger.api.v1.transaction_filter.{Filters, TransactionFilter}
import com.daml.ledger.api.v1.transaction_service.{
GetTransactionByEventIdRequest,
GetTransactionByIdRequest,
GetTransactionsRequest,
GetTransactionsResponse,
}
import com.daml.ledger.api.v1.value.{Identifier, Value}
import com.daml.ledger.api.v1.value.Value
import com.daml.ledger.client.binding.{Primitive, Template}
import com.daml.lf.data.Ref.HexString
import com.daml.timer.Delayed
@ -150,22 +151,29 @@ class TimeoutParticipantTestContext(timeoutScaleFactor: Double, delegate: Partic
)
override def activeContractsRequest(
parties: Seq[Primitive.Party],
templateIds: Seq[Identifier],
): GetActiveContractsRequest = delegate.activeContractsRequest(parties, templateIds)
templateIds: Seq[TemplateId],
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
): GetActiveContractsRequest =
delegate.activeContractsRequest(parties, templateIds, interfaceFilters)
override def activeContracts(parties: Primitive.Party*): Future[Vector[CreatedEvent]] =
withTimeout(s"Active contracts for parties $parties", delegate.activeContracts(parties: _*))
override def activeContractsByTemplateId(
templateIds: Seq[Identifier],
templateIds: Seq[TemplateId],
parties: Primitive.Party*
): Future[Vector[CreatedEvent]] = withTimeout(
s"Active contracts by template ids $templateIds for parties $parties",
delegate.activeContractsByTemplateId(templateIds, parties: _*),
)
override def getTransactionsRequest(
def transactionFilter(
parties: Seq[Primitive.Party],
templateIds: Seq[TemplateId],
templateIds: Seq[TemplateId] = Seq.empty,
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
) = delegate.transactionFilter(parties, templateIds, interfaceFilters)
override def getTransactionsRequest(
transactionFilter: TransactionFilter,
begin: LedgerOffset,
): GetTransactionsRequest = delegate.getTransactionsRequest(parties, templateIds, begin)
): GetTransactionsRequest = delegate.getTransactionsRequest(transactionFilter, begin)
override def transactionStream(
request: GetTransactionsRequest,
responseObserver: StreamObserver[GetTransactionsResponse],
@ -496,4 +504,8 @@ class TimeoutParticipantTestContext(timeoutScaleFactor: Double, delegate: Partic
)
}
override def filters(
templateIds: Seq[TemplateId],
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)],
): Filters = delegate.filters(templateIds, interfaceFilters)
}

View File

@ -119,7 +119,7 @@ class ActiveContractsServiceIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
(dummy, _, _) <- createDummyContracts(party, ledger)
activeContracts <- ledger.activeContractsByTemplateId(Seq(Dummy.id.unwrap), party)
activeContracts <- ledger.activeContractsByTemplateId(Seq(Dummy.id), party)
} yield {
assert(
activeContracts.size == 1,
@ -185,7 +185,7 @@ class ActiveContractsServiceIT extends LedgerTestSuite {
ledger.activeContractsRequest(Seq(party))
)
dummyWithParam <- ledger.create(party, DummyWithParam(party))
request = ledger.getTransactionsRequest(Seq(party))
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
fromOffset = request.update(_.begin := offset)
transactions <- ledger.flatTransactions(fromOffset)
} yield {
@ -253,9 +253,9 @@ class ActiveContractsServiceIT extends LedgerTestSuite {
allContractsForAlice <- ledger.activeContracts(alice)
allContractsForBob <- ledger.activeContracts(bob)
allContractsForAliceAndBob <- ledger.activeContracts(alice, bob)
dummyContractsForAlice <- ledger.activeContractsByTemplateId(Seq(Dummy.id.unwrap), alice)
dummyContractsForAlice <- ledger.activeContractsByTemplateId(Seq(Dummy.id), alice)
dummyContractsForAliceAndBob <- ledger.activeContractsByTemplateId(
Seq(Dummy.id.unwrap),
Seq(Dummy.id),
alice,
bob,
)

View File

@ -467,7 +467,7 @@ final class ContractKeysIT extends LedgerTestSuite {
_ <- synchronize(ledger1, ledger2)
} yield ()
})
import scalaz.syntax.tag._
test(
"CKDisclosedContractKeyReusabilityAsSubmitter",
"Subsequent disclosed contracts can use the same contract key (disclosure because of submitting)",
@ -488,7 +488,7 @@ final class ContractKeysIT extends LedgerTestSuite {
_ <- synchronize(ledger1, ledger2)
Seq(withKey1Event) <- ledger1.activeContractsByTemplateId(List(WithKey.id.unwrap), party1)
Seq(withKey1Event) <- ledger1.activeContractsByTemplateId(List(WithKey.id), party1)
withKey1 = ContractId.apply[WithKey](withKey1Event.contractId)
// Archive the disclosed contract
_ <- ledger1.exercise(party1, withKey1.exerciseArchive())

View File

@ -110,14 +110,14 @@ class ParticipantPruningIT extends LedgerTestSuite {
transactionsAfterPrune <- participant.transactionTrees(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetToPruneUpTo)
)
cannotReadAnymore <- participant
.transactionTrees(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetOfSecondToLastPrunedTransaction)
)
.mustFail("attempting to read transactions before the pruning cut-off")
@ -156,14 +156,14 @@ class ParticipantPruningIT extends LedgerTestSuite {
txAfterPrune <- participant.flatTransactions(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetToPruneUpTo)
)
cannotReadAnymore <- participant
.flatTransactions(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetOfSecondToLastPrunedTransaction)
)
.mustFail("attempting to read transactions before the pruning cut-off")
@ -452,7 +452,7 @@ class ParticipantPruningIT extends LedgerTestSuite {
transactionsAfterPrune <- participant.transactionTrees(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetToPruneUpTo)
)
@ -462,7 +462,7 @@ class ParticipantPruningIT extends LedgerTestSuite {
transactionsAfterRedundantPrune <- participant.transactionTrees(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetToPruneUpTo)
)
@ -476,7 +476,7 @@ class ParticipantPruningIT extends LedgerTestSuite {
transactionsAfterSecondPrune <- participant.transactionTrees(
participant
.getTransactionsRequest(parties = Seq(submitter))
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
.update(_.begin := offsetToPruneUpToInSecondRealPrune)
)
@ -542,11 +542,17 @@ class ParticipantPruningIT extends LedgerTestSuite {
_ <- participant.prune(offsetToPruneUpTo)
emptyRangeAtBegin = participant
.getTransactionsRequest(parties = Seq(submitter), begin = participant.begin)
.getTransactionsRequest(
participant.transactionFilter(parties = Seq(submitter)),
begin = participant.begin,
)
.update(_.end := participant.begin)
emptyRangeInPrunedSpace = participant
.getTransactionsRequest(parties = Seq(submitter), begin = offsetInPrunedRange)
.getTransactionsRequest(
participant.transactionFilter(parties = Seq(submitter)),
begin = offsetInPrunedRange,
)
.update(_.end := offsetInPrunedRange)
emptyBeginTreesWillFail <- participant.transactionTrees(emptyRangeAtBegin)
@ -802,7 +808,10 @@ class ParticipantPruningIT extends LedgerTestSuite {
} yield ()
})
trees <- participant.transactionTrees(
participant.getTransactionsRequest(parties = Seq(submitter), begin = endOffsetAtTestStart)
participant.getTransactionsRequest(
participant.transactionFilter(parties = Seq(submitter)),
begin = endOffsetAtTestStart,
)
)
} yield trees

View File

@ -29,13 +29,13 @@ class TransactionServiceCorrectnessIT extends LedgerTestSuite {
_ <- Future.sequence(Vector.fill(transactionsToSubmit)(ledger.create(party, Dummy(party))))
endAfterFirstSection <- ledger.currentEnd()
firstSectionRequest = ledger
.getTransactionsRequest(Seq(party))
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
.update(_.end := endAfterFirstSection)
firstSection <- ledger.flatTransactions(firstSectionRequest)
_ <- Future.sequence(Vector.fill(transactionsToSubmit)(ledger.create(party, Dummy(party))))
endAfterSecondSection <- ledger.currentEnd()
secondSectionRequest = ledger
.getTransactionsRequest(Seq(party))
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
.update(_.begin := endAfterFirstSection, _.end := endAfterSecondSection)
secondSection <- ledger.flatTransactions(secondSectionRequest)
fullSequence <- ledger.flatTransactions(party)

View File

@ -69,7 +69,7 @@ class TransactionServiceOutputsIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
_ <- ledger.create(party, Dummy(party))
request = ledger.getTransactionsRequest(Seq(party))
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
verboseTransactions <- ledger.flatTransactions(request.update(_.verbose := true))
verboseTransactionTrees <- ledger.transactionTrees(request.update(_.verbose := true))
nonVerboseTransactions <- ledger.flatTransactions(request.update(_.verbose := false))

View File

@ -20,7 +20,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
"An empty stream should be served when getting transactions from and to the beginning of the ledger",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val request = ledger.getTransactionsRequest(Seq(party))
val request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
val fromAndToBegin = request.update(_.begin := ledger.begin, _.end := ledger.begin)
for {
transactions <- ledger.flatTransactions(fromAndToBegin)
@ -37,7 +37,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
"An empty stream of trees should be served when getting transactions from and to the beginning of the ledger",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val request = ledger.getTransactionsRequest(Seq(party))
val request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
val fromAndToBegin = request.update(_.begin := ledger.begin, _.end := ledger.begin)
for {
transactions <- ledger.transactionTrees(fromAndToBegin)
@ -56,7 +56,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
_ <- ledger.create(party, Dummy(party))
request = ledger.getTransactionsRequest(Seq(party))
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
endToEnd = request.update(_.begin := ledger.end, _.end := ledger.end)
transactions <- ledger.flatTransactions(endToEnd)
} yield {
@ -75,7 +75,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
for {
_ <- ledger.create(party, Dummy(party))
futureOffset <- ledger.offsetBeyondLedgerEnd()
request = ledger.getTransactionsRequest(Seq(party))
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
beyondEnd = request.update(_.begin := futureOffset, _.optionalEnd := None)
failure <- ledger.flatTransactions(beyondEnd).mustFail("subscribing past the ledger end")
} yield {
@ -95,7 +95,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
for {
_ <- ledger.create(party, Dummy(party))
futureOffset <- ledger.offsetBeyondLedgerEnd()
request = ledger.getTransactionsRequest(Seq(party))
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
beyondEnd = request.update(_.begin := futureOffset, _.optionalEnd := None)
failure <- ledger.transactionTrees(beyondEnd).mustFail("subscribing past the ledger end")
} yield {

View File

@ -17,7 +17,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
"A query with an empty transaction filter should be rejected with an INVALID_ARGUMENT status",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val request = ledger.getTransactionsRequest(Seq(party))
val request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
val requestWithEmptyFilter = request.update(_.filter.filtersByParty := Map.empty)
for {
failure <- ledger
@ -41,7 +41,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
earlier <- ledger.currentEnd()
_ <- ledger.create(party, Dummy(party))
later <- ledger.currentEnd()
request = ledger.getTransactionsRequest(Seq(party))
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
invalidRequest = request.update(_.begin := later, _.end := earlier)
failure <- ledger
.flatTransactions(invalidRequest)
@ -62,7 +62,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val invalidLedgerId = "DEFINITELY_NOT_A_VALID_LEDGER_IDENTIFIER"
val invalidRequest = ledger
.getTransactionsRequest(Seq(party))
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
.update(_.ledgerId := invalidLedgerId)
for {
failure <- ledger
@ -84,7 +84,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val invalidLedgerId = "DEFINITELY_NOT_A_VALID_LEDGER_IDENTIFIER"
val invalidRequest = ledger
.getTransactionsRequest(Seq(party))
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
.update(_.ledgerId := invalidLedgerId)
for {
failure <- ledger

View File

@ -8,7 +8,10 @@ import com.daml.ledger.api.tls.TlsConfiguration
package object v1_dev {
def default(timeoutScaleFactor: Double): Vector[LedgerTestSuite] =
v1_14.default(timeoutScaleFactor) :+ new InterfaceIT
v1_14.default(timeoutScaleFactor) ++ Vector(
new InterfaceIT,
new InterfaceSubscriptionsIT,
)
def optional(tlsConfig: Option[TlsConfiguration]): Vector[LedgerTestSuite] =
v1_14.optional(tlsConfig)

View File

@ -0,0 +1,584 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.ledger.api.testtool.suites.v1_dev
import com.daml.error.definitions.LedgerApiErrors
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
import com.daml.ledger.api.testtool.infrastructure.Allocation.{
Participant,
Participants,
Parties,
SingleParty,
allocate,
}
import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.api.testtool.infrastructure.TransactionHelpers._
import com.daml.ledger.api.v1.event.Event.Event
import com.daml.ledger.api.v1.event.{CreatedEvent, InterfaceView}
import com.daml.ledger.api.v1.transaction.Transaction
import com.daml.ledger.api.v1.transaction_filter.TransactionFilter
import com.daml.ledger.api.v1.value.{Identifier, Record}
import com.daml.ledger.test.semantic.InterfaceViews._
import scalaz.Tag
class InterfaceSubscriptionsIT extends LedgerTestSuite {
test(
"ISTransactionsBasic",
"Basic functionality for interface subscriptions on transaction streams",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
c1 <- create(party, T1(party, 1))
c2 <- create(party, T2(party, 2))
c3 <- create(party, T3(party, 3))
_ <- create(party, T4(party, 4))
transactions <- flatTransactions(
getTransactionsRequest(
transactionFilter(
Seq(party),
Seq(T1.id),
Seq((I.id, true)),
)
)
)
events = transactions.flatMap(createdEvents)
} yield basicAssertions(c1.toString, c2.toString, c3.toString, events)
})
test(
"ISAcsBasic",
"Basic functionality for interface subscriptions on ACS streams",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
c1 <- create(party, T1(party, 1))
c2 <- create(party, T2(party, 2))
c3 <- create(party, T3(party, 3))
_ <- create(party, T4(party, 4))
(_, createdEvents) <- activeContracts(
activeContractsRequest(Seq(party), Seq(T1.id), Seq((I.id, true)))
)
} yield basicAssertions(c1.toString, c2.toString, c3.toString, createdEvents)
})
private def basicAssertions(
c1: String,
c2: String,
c3: String,
createdEvents: Vector[CreatedEvent],
): Unit = {
assertLength("3 transactions found", 3, createdEvents)
// T1
val createdEvent1 = createdEvents(0)
assertLength("Create event 1 has a view", 1, createdEvent1.interfaceViews)
assertEquals(
"Create event 1 template ID",
createdEvent1.templateId.get.toString,
Tag.unwrap(T1.id).toString,
)
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1)
assertViewEquals(createdEvent1.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View1 has 2 fields", 2, value.fields)
assertEquals("View1.a", value.fields(0).getValue.getInt64, 1)
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
assert(
value.fields.forall(_.label.nonEmpty),
"Expected a view with labels (verbose)",
)
}
assertEquals(
"Create event 1 createArguments must NOT be empty",
createdEvent1.createArguments.isEmpty,
false,
)
assert(
createdEvent1.getCreateArguments.fields.forall(_.label.nonEmpty),
"Expected a contract with labels (verbose)",
)
assertEquals(
"Create event 1 should have a contract key defined",
createdEvent1.contractKey.isDefined,
true,
)
// T2
val createdEvent2 = createdEvents(1)
assertLength("Create event 2 has a view", 1, createdEvent2.interfaceViews)
assertEquals(
"Create event 2 template ID",
createdEvent2.templateId.get.toString,
Tag.unwrap(T2.id).toString,
)
assertEquals("Create event 2 contract ID", createdEvent2.contractId, c2)
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View2 has 2 fields", 2, value.fields)
assertEquals("View2.a", value.fields(0).getValue.getInt64, 2)
assertEquals("View2.b", value.fields(1).getValue.getBool, false)
}
assertEquals(
"Create event 2 createArguments must be empty",
createdEvent2.createArguments.isEmpty,
true,
)
assertEquals(
"Create event 2 should have a contract key empty, as no match by template_id",
createdEvent2.contractKey.isEmpty,
true,
)
// T3
val createdEvent3 = createdEvents(2)
assertLength("Create event 3 has a view", 1, createdEvent3.interfaceViews)
assertEquals(
"Create event 3 template ID",
createdEvent3.templateId.get.toString,
Tag.unwrap(T3.id).toString,
)
assertEquals("Create event 3 contract ID", createdEvent3.contractId, c3)
assertViewFailed(createdEvent3.interfaceViews, Tag.unwrap(I.id))
assertEquals(
"Create event 3 createArguments must be empty",
createdEvent3.createArguments.isEmpty,
true,
)
}
test(
"ISMultipleWitness",
"Multiple witness",
allocate(Parties(2)),
)(implicit ec => { case Participants(Participant(ledger, party1, party2)) =>
import ledger._
for {
c <- create(party1, T6(party1, party2))
mergedTransactions <- flatTransactions(
getTransactionsRequest(
new TransactionFilter(
Map(
party1.toString -> filters(Seq.empty, Seq((I.id, true))),
party2.toString -> filters(Seq.empty, Seq((I2.id, true))),
)
)
)
)
party1Transactions <- flatTransactions(
getTransactionsRequest(
new TransactionFilter(
Map(
party1.toString -> filters(Seq.empty, Seq((I.id, true)))
)
)
)
)
} yield {
assertLength("single transaction found", 1, mergedTransactions)
val createdEvent1 = createdEvents(mergedTransactions(0)).head
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c.toString)
assertViewEquals(createdEvent1.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View1 has 2 fields", 2, value.fields)
assertEquals("View1.a", value.fields(0).getValue.getInt64, 6)
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
}
assertViewEquals(createdEvent1.interfaceViews, Tag.unwrap(I2.id)) { value =>
assertLength("View2 has 1 field", 1, value.fields)
assertEquals("View2.c", value.fields(0).getValue.getInt64, 7)
}
assertLength("single transaction found", 1, party1Transactions)
val createdEvent2 = createdEvents(party1Transactions(0)).head
assertEquals("Create event 1 contract ID", createdEvent2.contractId, c.toString)
assertLength("single view found", 1, createdEvent2.interfaceViews)
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View1 has 2 fields", 2, value.fields)
assertEquals("View1.a", value.fields(0).getValue.getInt64, 6)
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
}
}
})
test(
"ISMultipleViews",
"Multiple interface views populated for one event",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
c <- create(party, T5(party, 31337))
transactions <- flatTransactions(
getTransactionsRequest(
transactionFilter(
Seq(party),
Seq.empty,
Seq((I.id, true), (I2.id, true)),
)
)
)
} yield {
assertLength("single transaction found", 1, transactions)
val createdEvent = createdEvents(transactions(0)).head
assertEquals("Create event 1 contract ID", createdEvent.contractId, c.toString)
assertViewEquals(createdEvent.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View1 has 2 fields", 2, value.fields)
assertEquals("View1.a", value.fields(0).getValue.getInt64, 31337)
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
}
assertViewEquals(createdEvent.interfaceViews, Tag.unwrap(I2.id)) { value =>
assertLength("View2 has 1 field", 1, value.fields)
assertEquals("View2.c", value.fields(0).getValue.getInt64, 1)
}
}
})
test(
"ISTransactionsIrrelevantTransactions",
"Subscribing on transaction stream by interface with no relevant transactions",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
_ <- create(party, T4(party, 4))
transactions <- flatTransactions(
getTransactionsRequest(
transactionFilter(Seq(party), Seq.empty, Seq((INoTemplate.id, true)))
)
)
} yield {
assertLength("0 transactions should be found", 0, transactions)
()
}
})
test(
"ISTransactionsDuplicateInterfaceFilters",
"Subscribing on transaction stream by interface with duplicate filters and not verbose",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
c1 <- create(party, T1(party, 1))
c2 <- create(party, T2(party, 2))
c3 <- create(party, T3(party, 3))
_ <- create(party, T4(party, 4))
transactions <- flatTransactions(
getTransactionsRequest(
transactionFilter(
Seq(party),
Seq(T1.id),
Seq((I.id, false), (I.id, true)),
)
)
.update(_.verbose := false)
)
} yield {
val createdEvent1 = createdEvents(transactions(0)).head
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1.toString)
val createdEvent2 = createdEvents(transactions(1)).head
assertEquals("Create event 2 contract ID", createdEvent2.contractId, c2.toString)
// Expect view to be delivered even though there is an ambiguous
// includeInterfaceView flag set to true and false at the same time (true wins)
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View2 has 2 fields", 2, value.fields)
assertEquals("View2.a", value.fields(0).getValue.getInt64, 2)
assertEquals("View2.b", value.fields(1).getValue.getBool, false)
assert(
value.fields.forall(_.label.isEmpty),
s"Expected a view with no labels (verbose = false)",
)
}
val createdEvent3 = createdEvents(transactions(2)).head
assertEquals("Create event 3 contract ID", createdEvent3.contractId, c3.toString)
}
})
test(
"ISTransactionsDuplicateTemplateFilters",
"Subscribing on transaction stream by template with duplicate filters",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
c1 <- create(party, T1(party, 1))
c2 <- create(party, T2(party, 2))
c3 <- create(party, T3(party, 3))
_ <- create(party, T4(party, 4))
transactions <- flatTransactions(
getTransactionsRequest(
transactionFilter(
Seq(party),
Seq(T1.id, T1.id),
Seq((I.id, true)),
)
)
)
} yield {
val createdEvent1 = createdEvents(transactions(0)).head
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1.toString)
assertEquals(
"Create event 1 createArguments must NOT be empty",
createdEvent1.createArguments.isEmpty,
false,
)
val createdEvent2 = createdEvents(transactions(1)).head
assertEquals("Create event 2 contract ID", createdEvent2.contractId, c2.toString)
// Expect view to be delivered even though there is an ambiguous
// includeInterfaceView flag set to true and false at the same time.
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
assertLength("View2 has 2 fields", 2, value.fields)
assertEquals("View2.a", value.fields(0).getValue.getInt64, 2)
assertEquals("View2.b", value.fields(1).getValue.getBool, false)
}
val createdEvent3 = createdEvents(transactions(2)).head
assertEquals("Create event 3 contract ID", createdEvent3.contractId, c3.toString)
}
})
test(
"ISTransactionsNoIncludedView",
"Subscribing on transaction stream by interface or template without included views",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
for {
c1 <- create(party, T1(party, 1))
_ <- create(party, T2(party, 2))
_ <- create(party, T3(party, 3))
_ <- create(party, T4(party, 4))
transactions <- flatTransactions(
getTransactionsRequest(
transactionFilter(
Seq(party),
Seq(T1.id),
Seq((I.id, false)),
)
)
)
} yield {
assertLength("3 transactions found", 3, transactions)
val interfaceViewCount: Int =
transactions.flatMap(createdEvents).map(_.interfaceViews.size).sum
assertEquals("No views have been computed and produced", 0, interfaceViewCount)
val createArgumentsCount: Int =
transactions.flatMap(createdEvents).map(_.createArguments.isDefined).count(_ == true)
assertEquals("Only single create arguments must be delivered", 1, createArgumentsCount)
// T1
val createdEvent1 = createdEvents(transactions(0)).head
assertEquals(
"Create event 1 template ID",
createdEvent1.templateId.get.toString,
Tag.unwrap(T1.id).toString,
)
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1.toString)
assertEquals(
"Create event 1 createArguments must NOT be empty",
createdEvent1.createArguments.isEmpty,
false,
)
}
})
test(
"ISTransactionsEquivalentFilters",
"Subscribing by interface or all implementing templates gives the same result",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import ledger._
val allImplementations = Seq(T1.id, T2.id, T3.id)
for {
_ <- create(party, T1(party, 1))
_ <- create(party, T2(party, 2))
_ <- create(party, T3(party, 3))
_ <- create(party, T4(party, 4))
// 1. Subscribe by the interface
transactions1 <- flatTransactions(
getTransactionsRequest(
transactionFilter(Seq(party), Seq.empty, Seq((I.id, true)))
)
)
// 2. Subscribe by all implementing templates
transactions2 <- flatTransactions(
getTransactionsRequest(
transactionFilter(Seq(party), allImplementations, Seq.empty)
)
)
// 3. Subscribe by both the interface and all templates (redundant filters)
transactions3 <- flatTransactions(
getTransactionsRequest(
transactionFilter(
Seq(party),
allImplementations,
Seq((I.id, true)),
)
)
)
} yield {
assertLength("3 transactions found", 3, transactions1)
assertEquals(
"1 and 2 find the same transactions (but not the same views)",
transactions1.map(_.transactionId),
transactions2.map(_.transactionId),
)
assertEquals(
"2 and 3 find the same contract_arguments (but not the same views)",
transactions2.map(updateTransaction()),
transactions3.map(updateTransaction(emptyView = true)),
)
assertEquals(
"1 and 3 produce the same views (but not the same create arguments)",
transactions1.map(updateTransaction()),
transactions3.map(updateTransaction(emptyContractKey = true, emptyCreateArguments = true)),
)
}
})
test(
"ISTransactionsUnknownTemplateOrInterface",
"Subscribing on transaction stream by an unknown template fails",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val unknownTemplate = TemplateId(Tag.unwrap(I.id).copy(entityName = "IDoesNotExist"))
val unknownInterface = TemplateId(Tag.unwrap(I.id).copy(entityName = "IDoesNotExist"))
import ledger._
for {
_ <- create(party, T1(party, 1))
failure <- flatTransactions(
getTransactionsRequest(
transactionFilter(Seq(party), Seq(unknownTemplate), Seq.empty)
)
)
.mustFail("subscribing with an unknown template")
failure2 <- flatTransactions(
getTransactionsRequest(
transactionFilter(Seq(party), Seq.empty, Seq((unknownInterface, true)))
)
)
.mustFail("subscribing with an unknown interface")
} yield {
assertGrpcError(
failure,
LedgerApiErrors.RequestValidation.InvalidArgument,
Some(s"Templates do not exist"),
)
assertGrpcError(
failure2,
LedgerApiErrors.RequestValidation.InvalidArgument,
Some(s"Interfaces do not exist"),
)
}
})
test(
"ISTransactionsMultipleParties",
"Subscribing on transaction stream by multiple parties",
allocate(Parties(2)),
)(implicit ec => { case Participants(Participant(ledger, party1, party2)) =>
import ledger._
for {
_ <- create(party1, T6(party1, party2))
party1Iface1transactionsWithView <- flatTransactions(
getTransactionsRequest(transactionFilter(Seq(party1), Seq.empty, Seq((I.id, true))))
)
party1Iface1transactionsWithoutView <- flatTransactions(
getTransactionsRequest(transactionFilter(Seq(party1), Seq.empty, Seq((I.id, false))))
)
party2Iface1transactionsWithView <- flatTransactions(
getTransactionsRequest(transactionFilter(Seq(party2), Seq.empty, Seq((I.id, true))))
)
party2Iface1transactionsWithoutView <- flatTransactions(
getTransactionsRequest(transactionFilter(Seq(party2), Seq.empty, Seq((I.id, false))))
)
} yield {
assertEquals(
party1Iface1transactionsWithView.map(
updateTransaction(emptyView = false, emptyWitness = true)
),
party2Iface1transactionsWithView.map(
updateTransaction(emptyView = false, emptyWitness = true)
),
)
assertEquals(
party1Iface1transactionsWithoutView.map(
updateTransaction(emptyView = false, emptyWitness = true)
),
party2Iface1transactionsWithoutView.map(
updateTransaction(emptyView = false, emptyWitness = true)
),
)
assertEquals(
party1Iface1transactionsWithView.map(
updateTransaction(emptyView = true, emptyWitness = true)
),
party2Iface1transactionsWithoutView.map(
updateTransaction(emptyView = false, emptyWitness = true)
),
)
}
})
private def updateTransaction(
emptyView: Boolean = false,
emptyWitness: Boolean = false,
emptyCreateArguments: Boolean = false,
emptyContractKey: Boolean = false,
)(tx: com.daml.ledger.api.v1.transaction.Transaction): Transaction = {
tx.copy(
events = tx.events.map { event =>
event.copy(event = event.event match {
case created: Event.Created =>
created.copy(value =
created.value.copy(
witnessParties = if (emptyWitness) Seq.empty else created.value.witnessParties,
interfaceViews = if (emptyView) Seq.empty else created.value.interfaceViews,
contractKey = if (emptyContractKey) None else created.value.contractKey,
createArguments = if (emptyCreateArguments) None else created.value.createArguments,
)
)
case other => other
})
},
commandId = "",
)
}
private def assertViewFailed(views: Seq[InterfaceView], interfaceId: Identifier): Unit = {
val viewSearch = views.find(_.interfaceId.contains(interfaceId))
val view = assertDefined(viewSearch, "View could not be found")
val actualInterfaceId = assertDefined(view.interfaceId, "Interface ID is not defined")
assertEquals("View has correct interface ID", interfaceId, actualInterfaceId)
val status = assertDefined(view.viewStatus, "Status is not defined")
assertEquals("Status must be failed", status.code, 9)
}
private def assertViewEquals(views: Seq[InterfaceView], interfaceId: Identifier)(
checkValue: Record => Unit
): Unit = {
val viewSearch = views.find(_.interfaceId.contains(interfaceId))
val view = assertDefined(viewSearch, "View could not be found")
val viewCount = views.count(_.interfaceId.contains(interfaceId))
assertEquals(s"Only one view of interfaceId=$interfaceId must be defined", viewCount, 1)
val actualInterfaceId = assertDefined(view.interfaceId, "Interface ID is not defined")
assertEquals("View has correct interface ID", actualInterfaceId, interfaceId)
val status = assertDefined(view.viewStatus, "Status is not defined")
assertEquals("Status must be successful", status.code, 0)
val actualValue = assertDefined(view.viewValue, "Value is not defined")
checkValue(actualValue)
}
}

View File

@ -559,6 +559,23 @@ conformance_test(
],
)
# TODO Make sure InterfaceSubscriptionsIT is run on all servers, after Interface feature is on stable lf
server_conformance_test(
name = "conformance-test-interface-subscriptions",
hocon = True,
hocon_config = """
ledger.participants.default.api-server.seeding = testing-weak
""",
lf_versions = [
"1.dev",
],
servers = SERVERS,
test_tool_args = [
"--verbose",
"--include=InterfaceSubscriptionsIT",
],
)
exports_files([
"src/app/resources/logback.xml",
])

View File

@ -0,0 +1,104 @@
-- Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
module InterfaceViews where
data View = View with
a : Int
b : Bool
data View2 = View2 with
c : Int
data EmptyInterfaceView = EmptyInterfaceView {}
-- There are 3 interfaces defined:
-- 1) An interface implementing "View" view
-- 2) An interface implementing "View2" view (for variability)
-- 2) An interface with an empty interface view
interface I where
viewtype View
interface I2 where
viewtype View2
interface INoTemplate where
viewtype EmptyInterfaceView
-- Template defining a contract key, implementing interface "I".
template T1
with
p : Party
a : Int
where
key (p, a): (Party, Int)
maintainer key._1
signatory p
interface instance I for T1 where
view = View with
a
b = True
-- Template defining a contract key, implementing interface "I", with different values from "T1".
template T2
with
p : Party
a : Int
where
key (p, a): (Party, Int)
maintainer key._1
signatory p
interface instance I for T2 where
view = View with
a
b = False
-- Template defining a contract key, implementing interface "I", which is crashing.
template T3
with
p : Party
a : Int
where
signatory p
interface instance I for T3 where
view = error "view crashed"
-- Template which is unrelated to any of the defined interfaces.
template T4
with
p : Party
a : Int
where
signatory p
-- Template defining a contract key, implementing 2 interfaces: "I" and "I2"
template T5
with
p : Party
a : Int
where
signatory p
interface instance I for T5 where
view = View with
a
b = True
interface instance I2 for T5 where
view = View2 with
c = 1
-- Template defining a contract key, with multiple witnesses, implementing 2 interfaces: "I" and "I2"
template T6
with
p1 : Party
p2 : Party
where
signatory p1
observer p1, p2
interface instance I for T6 where
view = View with
a = 6
b = True
interface instance I2 for T6 where
view = View2 with
c = 7