update canton to be4893cc (#17678)

CHANGELOG_BEGIN
CHANGELOG_END

Co-authored-by: Azure Pipelines Daml Build <support@digitalasset.com>
This commit is contained in:
azure-pipelines[bot] 2023-10-27 07:40:00 +00:00 committed by GitHub
parent b12eb2adb6
commit 16aa217692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 921 additions and 633 deletions

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
sandbox-options:
- --wall-clock-time
name: contact

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
sandbox-options:
- --wall-clock-time
name: message

View File

@ -17,17 +17,18 @@ message TransferOutView {
com.digitalasset.canton.crypto.v0.Salt salt = 1;
string submitter = 2;
string contract_id = 3;
string template_id = 4; // added in v2
string target_domain = 5;
com.digitalasset.canton.time.v0.TimeProof target_time_proof = 6;
int32 target_protocol_version = 7;
string submitting_participant = 8; // added in v2
string application_id = 9; // added in v2
string submission_id = 10; // optional - added in v2
string workflow_id = 11; // optional - added in v2
string command_id = 12; // added in v2
int64 transfer_counter = 13; // added in v2
reserved 3; // contract_id is now contained in contract
string target_domain = 4;
com.digitalasset.canton.time.v0.TimeProof target_time_proof = 5;
int32 target_protocol_version = 6;
string submitting_participant = 7; // added in v2
string application_id = 8; // added in v2
string submission_id = 9; // optional - added in v2
string workflow_id = 10; // optional - added in v2
string command_id = 11; // added in v2
int64 transfer_counter = 12; // added in v2
bytes creating_transaction_id = 13; // added in v2
com.digitalasset.canton.protocol.v1.SerializableContract contract = 14; // added in v2
}
message TransferInView {

View File

@ -11,17 +11,7 @@ import com.digitalasset.canton.crypto.*
import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting}
import com.digitalasset.canton.protocol.ContractIdSyntax.*
import com.digitalasset.canton.protocol.messages.TransferOutMediatorMessage
import com.digitalasset.canton.protocol.{
LfContractId,
LfTemplateId,
RootHash,
SourceDomainId,
TargetDomainId,
ViewHash,
v0,
v1,
v2,
}
import com.digitalasset.canton.protocol.{v0, *}
import com.digitalasset.canton.serialization.ProtoConverter.ParsingResult
import com.digitalasset.canton.serialization.{ProtoConverter, ProtocolVersionedMemoizedEvidence}
import com.digitalasset.canton.time.TimeProof
@ -29,16 +19,7 @@ import com.digitalasset.canton.topology.transaction.TrustLevel
import com.digitalasset.canton.topology.{DomainId, MediatorRef}
import com.digitalasset.canton.util.EitherUtil
import com.digitalasset.canton.version.Transfer.{SourceProtocolVersion, TargetProtocolVersion}
import com.digitalasset.canton.version.{
HasMemoizedProtocolVersionedWithContextCompanion,
HasProtocolVersionedWithContextCompanion,
HasProtocolVersionedWrapper,
HasRepresentativeProtocolVersion,
HasVersionedToByteString,
ProtoVersion,
ProtocolVersion,
RepresentativeProtocolVersion,
}
import com.digitalasset.canton.version.*
import com.digitalasset.canton.{
LedgerApplicationId,
LedgerCommandId,
@ -369,38 +350,40 @@ object TransferOutCommonData
}
/** Aggregates the data of a transfer-out request that is only sent to the involved participants
*
* @param salt The salt to blind the Merkle hash
* @param submitterMetadata The metadata of the submitter of the transfer-out request
* @param contractId The contract ID to be transferred
* @param templateId The template ID of the contract to be transferred
* @param targetDomain The target domain to which the contract is to be transferred
* @param targetTimeProof The sequenced event from the target domain
* whose timestamp defines the baseline for measuring time periods on the target domain
* @param transferCounter The [[com.digitalasset.canton.TransferCounter]] of the contract.
* The value is defined iff the protocol versions is at least
* [[com.digitalasset.canton.version.ProtocolVersion.CNTestNet]].
*/
final case class TransferOutView private (
override val salt: Salt,
submitterMetadata: TransferSubmitterMetadata,
contractId: LfContractId,
templateId: LfTemplateId,
targetDomain: TargetDomainId,
targetTimeProof: TimeProof,
targetProtocolVersion: TargetProtocolVersion,
// TODO(#9014) Remove the option
transferCounter: TransferCounterO,
)(
hashOps: HashOps,
override val representativeProtocolVersion: RepresentativeProtocolVersion[TransferOutView.type],
override val deserializedFrom: Option[ByteString],
) extends MerkleTreeLeaf[TransferOutView](hashOps)
sealed abstract class TransferOutView(hashOps: HashOps)
extends MerkleTreeLeaf[TransferOutView](hashOps)
with HasProtocolVersionedWrapper[TransferOutView]
with ProtocolVersionedMemoizedEvidence {
// Ensures the invariants related to default values hold
validateInstance().valueOr(err => throw new IllegalArgumentException(err))
/** The salt used to blind the Merkle hash. */
def salt: Salt
def submitterMetadata: TransferSubmitterMetadata
/** The id of the contract to be transferred. */
def contractId: LfContractId
/** The template of the contract to be transferred.
* This is a dummy value until protocol version 4.
*/
def templateId: LfTemplateId
/** The domain to which the contract is transferred. */
def targetDomain: TargetDomainId
/** The sequenced event from the target domain
* whose timestamp defines the baseline for measuring time periods on the target domain
*/
def targetTimeProof: TimeProof
def targetProtocolVersion: TargetProtocolVersion
/** The [[com.digitalasset.canton.TransferCounter]] of the contract.
* The value is defined iff the protocol versions is at least
* [[com.digitalasset.canton.version.ProtocolVersion.CNTestNet]].
*/
def transferCounterO: TransferCounterO
val submitter: LfPartyId = submitterMetadata.submitter
val submittingParticipant: LedgerParticipantId = submitterMetadata.submittingParticipant
@ -409,10 +392,39 @@ final case class TransferOutView private (
val commandId: LedgerCommandId = submitterMetadata.commandId
val workflowId: Option[LfWorkflowId] = submitterMetadata.workflowId
override def hashPurpose: HashPurpose = HashPurpose.TransferOutView
def hashPurpose: HashPurpose = HashPurpose.TransferOutView
@transient override protected lazy val companionObj: TransferOutView.type = TransferOutView
override protected[this] def toByteStringUnmemoized: ByteString =
super[HasProtocolVersionedWrapper].toByteString
protected def toProtoV0: v0.TransferOutView
protected def toProtoV1: v1.TransferOutView
protected def toProtoV2: v2.TransferOutView
}
final case class TransferOutViewV0 private[data] (
override val salt: Salt,
submitterMetadata: TransferSubmitterMetadata,
contractId: LfContractId,
targetDomain: TargetDomainId,
targetTimeProof: TimeProof,
)(
hashOps: HashOps,
override val representativeProtocolVersion: RepresentativeProtocolVersion[TransferOutView.type],
override val deserializedFrom: Option[ByteString],
) extends TransferOutView(hashOps) {
override def templateId: LfTemplateId = TransferOutView.templateIdDefaultValue.defaultValue
override def targetProtocolVersion: TargetProtocolVersion =
TargetProtocolVersion(ProtocolVersion.v3)
override def transferCounterO: TransferCounterO = None
protected def toProtoV0: v0.TransferOutView =
v0.TransferOutView(
salt = Some(salt.toProtoV0),
@ -422,6 +434,46 @@ final case class TransferOutView private (
targetTimeProof = Some(targetTimeProof.toProtoV0),
)
protected def toProtoV1: v1.TransferOutView = throw new UnsupportedOperationException(
"Serialization to V1 not supported."
)
protected def toProtoV2: v2.TransferOutView = throw new UnsupportedOperationException(
"Serialization to V2 not supported."
)
override def pretty: Pretty[TransferOutViewV0] = prettyOfClass(
param("submitterMetadata", _.submitterMetadata),
param("contract id", _.contractId),
param("template id", _.templateId),
param("target domain", _.targetDomain),
param("target time proof", _.targetTimeProof),
param("target protocol version", _.targetProtocolVersion.v),
param("salt", _.salt),
)
}
final case class TransferOutViewV4 private[data] (
override val salt: Salt,
submitterMetadata: TransferSubmitterMetadata,
contractId: LfContractId,
targetDomain: TargetDomainId,
targetTimeProof: TimeProof,
targetProtocolVersion: TargetProtocolVersion,
)(
hashOps: HashOps,
override val representativeProtocolVersion: RepresentativeProtocolVersion[TransferOutView.type],
override val deserializedFrom: Option[ByteString],
) extends TransferOutView(hashOps) {
override def templateId: LfTemplateId = TransferOutView.templateIdDefaultValue.defaultValue
override def transferCounterO: TransferCounterO = None
protected def toProtoV0: v0.TransferOutView = throw new UnsupportedOperationException(
"Serialization to V0 not supported."
)
protected def toProtoV1: v1.TransferOutView =
v1.TransferOutView(
salt = Some(salt.toProtoV0),
@ -432,11 +484,55 @@ final case class TransferOutView private (
targetProtocolVersion = targetProtocolVersion.v.toProtoPrimitive,
)
protected def toProtoV2: v2.TransferOutView = throw new UnsupportedOperationException(
"Serialization to V2 not supported."
)
override def pretty: Pretty[TransferOutViewV4] = prettyOfClass(
param("submitterMetadata", _.submitterMetadata),
param("contract id", _.contractId),
param("template id", _.templateId),
param("target domain", _.targetDomain),
param("target time proof", _.targetTimeProof),
param("target protocol version", _.targetProtocolVersion.v),
param("salt", _.salt),
)
}
final case class TransferOutViewCNTestNet private[data] (
salt: Salt,
submitterMetadata: TransferSubmitterMetadata,
creatingTransactionId: TransactionId,
contract: SerializableContract,
targetDomain: TargetDomainId,
targetTimeProof: TimeProof,
targetProtocolVersion: TargetProtocolVersion,
transferCounter: TransferCounter,
)(
hashOps: HashOps,
override val representativeProtocolVersion: RepresentativeProtocolVersion[TransferOutView.type],
override val deserializedFrom: Option[ByteString],
) extends TransferOutView(hashOps) {
override def contractId: LfContractId = contract.contractId
override def templateId: LfTemplateId =
contract.rawContractInstance.contractInstance.unversioned.template
override def transferCounterO: TransferCounterO = Some(transferCounter)
protected def toProtoV0: v0.TransferOutView = throw new UnsupportedOperationException(
"Serialization to V0 not supported."
)
protected def toProtoV1: v1.TransferOutView = throw new UnsupportedOperationException(
"Serialization to V1 not supported."
)
protected def toProtoV2: v2.TransferOutView =
v2.TransferOutView(
salt = Some(salt.toProtoV0),
submitter = submitter,
contractId = contractId.toProtoPrimitive,
targetDomain = targetDomain.toProtoPrimitive,
targetTimeProof = Some(targetTimeProof.toProtoV0),
targetProtocolVersion = targetProtocolVersion.v.toProtoPrimitive,
@ -445,30 +541,20 @@ final case class TransferOutView private (
submissionId = submissionId.getOrElse(""),
workflowId = workflowId.getOrElse(""),
commandId = commandId,
templateId = templateId.toString,
transferCounter = transferCounter
.getOrElse(
throw new IllegalStateException(
s"Transfer counter must be defined at representative protocol version ${representativeProtocolVersion}"
)
)
.toProtoPrimitive,
transferCounter = transferCounter.toProtoPrimitive,
creatingTransactionId = creatingTransactionId.toProtoPrimitive,
contract = Some(contract.toProtoV1),
)
override protected[this] def toByteStringUnmemoized: ByteString =
super[HasProtocolVersionedWrapper].toByteString
override def pretty: Pretty[TransferOutView] = prettyOfClass(
param("submitter", _.submitter),
override def pretty: Pretty[TransferOutViewCNTestNet] = prettyOfClass(
param("submitterMetadata", _.submitterMetadata),
param("contract id", _.contractId),
param("template id", _.templateId),
param("creatingTransactionId", _.creatingTransactionId),
param("contract", _.contract),
param("target domain", _.targetDomain),
paramIfDefined("transfer counter", _.transferCounter),
param("target time proof", _.targetTimeProof),
param("submitting participant", _.submittingParticipant),
param("application id", _.applicationId),
paramIfDefined("submission id", _.submissionId),
paramIfDefined("workflow id", _.workflowId),
param("target protocol version", _.targetProtocolVersion.v),
param("salt", _.salt),
)
}
@ -477,7 +563,7 @@ object TransferOutView
extends HasMemoizedProtocolVersionedWithContextCompanion[TransferOutView, HashOps] {
override val name: String = "TransferOutView"
private[TransferOutView] final case class ParsedDataV0V1V2(
private[TransferOutView] final case class ParsedDataV0V1(
salt: Salt,
submitter: LfPartyId,
contractId: LfContractId,
@ -485,7 +571,7 @@ object TransferOutView
targetDomainPV: TargetProtocolVersion,
targetTimeProof: TimeProof,
)
private[TransferOutView] object ParsedDataV0V1V2 {
private[TransferOutView] object ParsedDataV0V1 {
def fromProto(
hashOps: HashOps,
saltP: Option[com.digitalasset.canton.crypto.v0.Salt],
@ -494,7 +580,7 @@ object TransferOutView
targetDomainP: String,
targetTimeProofP: Option[com.digitalasset.canton.time.v0.TimeProof],
targetProtocolVersion: ProtocolVersion,
): ParsingResult[ParsedDataV0V1V2] = {
): ParsingResult[ParsedDataV0V1] = {
for {
salt <- ProtoConverter.parseRequired(Salt.fromProtoV0, "salt", saltP)
submitter <- ProtoConverter.parseLfPartyId(submitterP)
@ -504,7 +590,7 @@ object TransferOutView
targetTimeProof <- ProtoConverter
.required("targetTimeProof", targetTimeProofP)
.flatMap(TimeProof.fromProtoV0(targetProtocolVersion, hashOps))
} yield ParsedDataV0V1V2(
} yield ParsedDataV0V1(
salt,
submitter,
contractId,
@ -530,24 +616,12 @@ object TransferOutView
),
)
override lazy val invariants = Seq(
transferCounterInvariant,
templateIdDefaultValue,
targetProtocolVersionDefaultValue,
)
private lazy val rpv4: RepresentativeProtocolVersion[TransferOutView.type] =
protocolVersionRepresentativeFor(ProtocolVersion.v4)
private lazy val rpvMultidomain: RepresentativeProtocolVersion[TransferOutView.type] =
protocolVersionRepresentativeFor(ProtocolVersion.CNTestNet)
lazy val transferCounterInvariant = EmptyOptionExactlyUntilExclusive(
_.transferCounter,
"transferCounter",
protocolVersionRepresentativeFor(TransferCommonData.minimumPvForTransferCounter),
)
lazy val submittingParticipantDefaultValue: DefaultValueUntilExclusive[LedgerParticipantId] =
DefaultValueUntilExclusive(
_.submitterMetadata.submittingParticipant,
@ -607,34 +681,50 @@ object TransferOutView
def create(hashOps: HashOps)(
salt: Salt,
submitterMetadata: TransferSubmitterMetadata,
contractId: LfContractId,
templateId: LfTemplateId,
creatingTransactionId: TransactionId,
contract: SerializableContract,
targetDomain: TargetDomainId,
targetTimeProof: TimeProof,
sourceProtocolVersion: SourceProtocolVersion,
targetProtocolVersion: TargetProtocolVersion,
transferCounter: TransferCounterO,
): Either[String, TransferOutView] = Either
.catchOnly[IllegalArgumentException](
TransferOutView(
): TransferOutView =
if (sourceProtocolVersion.v < ProtocolVersion.v4)
TransferOutViewV0(
salt,
submitterMetadata,
contractId,
templateId,
contract.contractId,
targetDomain,
targetTimeProof,
)(hashOps, protocolVersionRepresentativeFor(sourceProtocolVersion.v), None)
else if (sourceProtocolVersion.v < ProtocolVersion.CNTestNet)
TransferOutViewV4(
salt,
submitterMetadata,
contract.contractId,
targetDomain,
targetTimeProof,
targetProtocolVersion,
transferCounter,
)(hashOps, protocolVersionRepresentativeFor(sourceProtocolVersion.v), None)
)
.leftMap(_.getMessage)
else
TransferOutViewCNTestNet(
salt,
submitterMetadata,
creatingTransactionId,
contract,
targetDomain,
targetTimeProof,
targetProtocolVersion,
transferCounter.getOrElse(throw new IllegalArgumentException("Missing transfer counter.")),
)(hashOps, protocolVersionRepresentativeFor(sourceProtocolVersion.v), None)
private[this] def fromProtoV0(hashOps: HashOps, transferOutViewP: v0.TransferOutView)(
bytes: ByteString
): ParsingResult[TransferOutView] = {
): ParsingResult[TransferOutViewV0] = {
val v0.TransferOutView(saltP, submitterP, contractIdP, targetDomainP, targetTimeProofP) =
transferOutViewP
for {
commonData <- ParsedDataV0V1V2.fromProto(
commonData <- ParsedDataV0V1.fromProto(
hashOps,
saltP,
submitterP,
@ -643,7 +733,7 @@ object TransferOutView
targetTimeProofP,
ProtocolVersion.v3,
)
} yield TransferOutView(
} yield TransferOutViewV0(
commonData.salt,
TransferSubmitterMetadata(
commonData.submitter,
@ -654,11 +744,8 @@ object TransferOutView
workflowId = workflowIdDefaultValue.defaultValue,
),
commonData.contractId,
templateIdDefaultValue.defaultValue,
commonData.targetDomain,
commonData.targetTimeProof,
commonData.targetDomainPV,
None,
)(
hashOps,
protocolVersionRepresentativeFor(ProtoVersion(0)), // TODO(#12626)
@ -668,7 +755,7 @@ object TransferOutView
private[this] def fromProtoV1(hashOps: HashOps, transferOutViewP: v1.TransferOutView)(
bytes: ByteString
): ParsingResult[TransferOutView] = {
): ParsingResult[TransferOutViewV4] = {
val v1.TransferOutView(
saltP,
submitterP,
@ -679,7 +766,7 @@ object TransferOutView
) = transferOutViewP
for {
commonData <- ParsedDataV0V1V2.fromProto(
commonData <- ParsedDataV0V1.fromProto(
hashOps,
saltP,
submitterP,
@ -688,7 +775,7 @@ object TransferOutView
targetTimeProofP,
ProtocolVersion.fromProtoPrimitive(targetProtocolVersionP),
)
} yield TransferOutView(
} yield TransferOutViewV4(
commonData.salt,
TransferSubmitterMetadata(
commonData.submitter,
@ -699,11 +786,9 @@ object TransferOutView
workflowId = workflowIdDefaultValue.defaultValue,
),
commonData.contractId,
templateIdDefaultValue.defaultValue,
commonData.targetDomain,
commonData.targetTimeProof,
commonData.targetDomainPV,
None,
)(
hashOps,
protocolVersionRepresentativeFor(ProtoVersion(1)), // TODO(#12626)
@ -713,12 +798,10 @@ object TransferOutView
private[this] def fromProtoV2(hashOps: HashOps, transferOutViewP: v2.TransferOutView)(
bytes: ByteString
): ParsingResult[TransferOutView] = {
): ParsingResult[TransferOutViewCNTestNet] = {
val v2.TransferOutView(
saltP,
submitterP,
contractIdP,
templateIdP,
targetDomainP,
targetTimeProofP,
targetProtocolVersionP,
@ -728,41 +811,44 @@ object TransferOutView
workflowIdP,
commandIdP,
transferCounter,
creatingTransactionIdP,
contractPO,
) = transferOutViewP
for {
commonData <- ParsedDataV0V1V2.fromProto(
hashOps,
saltP,
submitterP,
contractIdP,
targetDomainP,
targetTimeProofP,
ProtocolVersion.fromProtoPrimitive(targetProtocolVersionP),
)
submittingParticipantId <-
ProtoConverter.parseLfParticipantId(submittingParticipantP)
salt <- ProtoConverter.parseRequired(Salt.fromProtoV0, "salt", saltP)
submitter <- ProtoConverter.parseLfPartyId(submitterP)
targetDomain <- DomainId.fromProtoPrimitive(targetDomainP, "targetDomain")
targetProtocolVersion = ProtocolVersion.fromProtoPrimitive(targetProtocolVersionP)
targetTimeProof <- ProtoConverter
.required("targetTimeProof", targetTimeProofP)
.flatMap(TimeProof.fromProtoV0(targetProtocolVersion, hashOps))
submittingParticipant <- ProtoConverter.parseLfParticipantId(submittingParticipantP)
applicationId <- ProtoConverter.parseLFApplicationId(applicationIdP)
submissionId <- ProtoConverter.parseLFSubmissionIdO(submissionIdP)
workflowId <- ProtoConverter.parseLFWorkflowIdO(workflowIdP)
commandId <- ProtoConverter.parseCommandId(commandIdP)
templateId <- ProtoConverter.parseTemplateId(templateIdP)
} yield TransferOutView(
commonData.salt,
creatingTransactionId <- TransactionId.fromProtoPrimitive(creatingTransactionIdP)
contract <- ProtoConverter
.required("TransferOutViewTree.contract", contractPO)
.flatMap(SerializableContract.fromProtoV1)
} yield TransferOutViewCNTestNet(
salt,
TransferSubmitterMetadata(
commonData.submitter,
submitter,
applicationId,
submittingParticipantId,
submittingParticipant,
commandId,
submissionId,
workflowId,
),
commonData.contractId,
templateId,
commonData.targetDomain,
commonData.targetTimeProof,
commonData.targetDomainPV,
Some(TransferCounter(transferCounter)),
creatingTransactionId,
contract,
TargetDomainId(targetDomain),
targetTimeProof,
TargetProtocolVersion(targetProtocolVersion),
TransferCounter(transferCounter),
)(
hashOps,
protocolVersionRepresentativeFor(ProtoVersion(2)), // TODO(#12626)
@ -797,7 +883,8 @@ final case class FullTransferOutTree(tree: TransferOutViewTree)
def contractId: LfContractId = view.contractId
def templateId: LfTemplateId = view.templateId
def transferCounter: TransferCounterO = view.transferCounter
def transferCounter: TransferCounterO = view.transferCounterO
def sourceDomain: SourceDomainId = commonData.sourceDomain

View File

@ -6,18 +6,17 @@ package com.digitalasset.canton.lifecycle
import cats.data.EitherT
import cats.syntax.traverse.*
import com.digitalasset.canton.DiscardOps
import com.digitalasset.canton.concurrent.{FutureSupervisor, Threading}
import com.digitalasset.canton.concurrent.Threading
import com.digitalasset.canton.config.ProcessingTimeout
import com.digitalasset.canton.lifecycle.FlagCloseable.forceShutdownStr
import com.digitalasset.canton.logging.{ErrorLoggingContext, TracedLogger}
import com.digitalasset.canton.logging.TracedLogger
import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.util.Thereafter.syntax.*
import com.digitalasset.canton.util.{Checked, CheckedT, Thereafter}
import org.slf4j.event.Level
import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}
import scala.collection.immutable.MultiSet
import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration}
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
import scala.util.control.NonFatal
@ -336,37 +335,6 @@ object CloseContext {
}
/** Mix-in to obtain a [[CloseContext]] implicit based on the class's [[FlagCloseable]] */
trait HasCloseContext extends PromiseUnlessShutdownFactory { self: FlagCloseable =>
trait HasCloseContext { self: FlagCloseable =>
implicit val closeContext: CloseContext = CloseContext(self)
}
trait PromiseUnlessShutdownFactory { self: HasCloseContext =>
protected def logger: TracedLogger
/** Use this method to create a PromiseUnlessShutdown that will automatically be cancelled when the close context
* is closed. This allows proper clean up of stray promises when the node is transitioning to a passive state.
*/
def mkPromise[A](
description: String,
futureSupervisor: FutureSupervisor,
logAfter: Duration = 10.seconds,
logLevel: Level = Level.DEBUG,
)(implicit elc: ErrorLoggingContext, ec: ExecutionContext): PromiseUnlessShutdown[A] = {
val promise = new PromiseUnlessShutdown[A](description, futureSupervisor, logAfter, logLevel)
val cancelToken = closeContext.flagCloseable.runOnShutdown(new RunOnShutdown {
override def name: String = s"$description-abort-promise-on-shutdown"
override def done: Boolean = promise.isCompleted
override def run(): Unit = promise.shutdown()
})(elc.traceContext)
promise.future
.onComplete { _ =>
Try(closeContext.flagCloseable.cancelShutdownTask(cancelToken)).failed.foreach(e =>
logger.debug(s"Failed to cancel shutdown task for $description", e)(elc.traceContext)
)
}
promise
}
}

View File

@ -19,7 +19,7 @@ import java.util.concurrent.atomic.AtomicReference
import scala.annotation.tailrec
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import scala.util.{Failure, Success, Try}
import scala.util.{Failure, Success}
/** Functions executed with this class will only run when all previous calls have completed executing.
* This can be used when async code should not be run concurrently.
@ -66,14 +66,10 @@ class SimpleExecutionQueue(
)(implicit loggingContext: ErrorLoggingContext): EitherT[FutureUnlessShutdown, A, B] =
EitherT(executeUS(execution.value, description))
def executeUS[A](
execution: => FutureUnlessShutdown[A],
description: String,
runWhenUnderFailures: => Unit = (),
)(implicit
def executeUS[A](execution: => FutureUnlessShutdown[A], description: String)(implicit
loggingContext: ErrorLoggingContext
): FutureUnlessShutdown[A] =
genExecute(runIfFailed = false, execution, description, runWhenUnderFailures)
genExecute(runIfFailed = false, execution, description)
def executeUnderFailures[A](execution: => Future[A], description: String)(implicit
loggingContext: ErrorLoggingContext
@ -93,7 +89,6 @@ class SimpleExecutionQueue(
runIfFailed: Boolean,
execution: => FutureUnlessShutdown[A],
description: String,
runWhenUnderFailures: => Unit = (),
)(implicit loggingContext: ErrorLoggingContext): FutureUnlessShutdown[A] = {
val next = new TaskCell(description, logTaskTiming, futureSupervisor, directExecutionContext)
val oldHead = queueHead.getAndSet(next) // linearization point
@ -105,7 +100,6 @@ class SimpleExecutionQueue(
directExecutionContext,
loggingContext.traceContext,
),
runWhenUnderFailures,
)
}
@ -234,7 +228,6 @@ object SimpleExecutionQueue {
pred: TaskCell,
runIfFailed: Boolean,
execution: => FutureUnlessShutdown[A],
runWhenUnderFailures: => Unit,
)(implicit
loggingContext: ErrorLoggingContext
): FutureUnlessShutdown[A] = {
@ -286,13 +279,6 @@ object SimpleExecutionQueue {
s"Not running task ${description.singleQuoted} due to exception after waiting for $waitingDelay"
)(loggingContext.traceContext)
}
Try(runWhenUnderFailures).failed
.foreach(e =>
loggingContext.logger.debug(
s"Failed to run 'runWhenUnderFailures' function for ${description.singleQuoted}",
e,
)(loggingContext.traceContext)
)
FutureUnlessShutdown.failed(ex)
}
}(directExecutionContext)

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
build-options:
- --target=1.14
name: CantonExamples

View File

@ -6,7 +6,7 @@ package com.digitalasset.canton.data
import com.daml.metrics.api.MetricHandle.Counter
import com.daml.metrics.api.MetricHandle.Gauge.CloseableGauge
import com.daml.nameof.NameOf.functionFullName
import com.digitalasset.canton.concurrent.{DirectExecutionContext, FutureSupervisor}
import com.digitalasset.canton.concurrent.FutureSupervisor
import com.digitalasset.canton.config.ProcessingTimeout
import com.digitalasset.canton.data.PeanoQueue.{BeforeHead, InsertedValue, NotInserted}
import com.digitalasset.canton.lifecycle.{FlagCloseable, FutureUnlessShutdown, Lifecycle}
@ -22,7 +22,6 @@ import java.util.concurrent.atomic.AtomicReference
import scala.annotation.tailrec
import scala.collection.mutable
import scala.concurrent.{ExecutionContext, Future, Promise, blocking}
import scala.util.control.NonFatal
/** The task scheduler manages tasks with associated timestamps and sequencer counters.
* Tasks may be inserted in any order; they will be executed nevertheless in the correct order
@ -298,18 +297,10 @@ class TaskScheduler[Task <: TaskScheduler.TimedTask](
case Some(tracedTask) =>
tracedTask.withTraceContext { implicit traceContext => task =>
FutureUtil.doNotAwait(
// Close the task if the queue is shutdown or if it has failed
// Close the task if the queue is shutdown
queue
.executeUS(task.perform(), task.toString)
.onShutdown(task.close())
.recoverWith {
// If any task fails, none of subsequent tasks will be executed so we might as well close the scheduler
// to force completion of the tasks and signal that the scheduler is not functional
case NonFatal(e) if !this.isClosing =>
this.close()
Future.failed(e)
// Use a direct context here to avoid closing the scheduler in a different thread
}(DirectExecutionContext(errorLoggingContext(traceContext).noTracingLogger)),
.onShutdown(task.close()),
show"A task failed with an exception.\n$task",
)
taskQueue.dequeue()

View File

@ -269,11 +269,8 @@ object GeneratorsTransferData {
submitterMetadata <- transferOutSubmitterMetadataGen(sourceProtocolVersion)
contractId <- Arbitrary.arbitrary[LfContractId]
templateId <- defaultValueGen(
sourceProtocolVersion.v,
TransferOutView.templateIdDefaultValue,
)(implicitly[Arbitrary[LfTemplateId]])
creatingTransactionId <- Arbitrary.arbitrary[TransactionId]
contract <- GeneratorsProtocol.serializableContractGen(sourceProtocolVersion.v)
targetDomain <- Arbitrary.arbitrary[TargetDomainId]
timeProof <- Arbitrary.arbitrary[TimeProof]
@ -285,15 +282,14 @@ object GeneratorsTransferData {
.create(hashOps)(
salt,
submitterMetadata,
contractId,
templateId,
creatingTransactionId,
contract,
targetDomain,
timeProof,
sourceProtocolVersion,
targetProtocolVersion,
transferCounter,
)
.value
)
}

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
name: ai-analysis
source: AIAnalysis.daml
init-script: AIAnalysis:setup

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
name: bank
source: Bank.daml
init-script: Bank:setup

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
name: doctor
source: Doctor.daml
init-script: Doctor:setup

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
name: health-insurance
source: HealthInsurance.daml
init-script: HealthInsurance:setup

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
name: medical-records
source: MedicalRecord.daml
init-script: MedicalRecord:setup

View File

@ -8,6 +8,7 @@ import com.daml.ledger.api.v1.transaction.{TransactionTree, TreeEvent}
import com.daml.ledger.api.v1.value.Value
import com.digitalasset.canton.concurrent.Threading
import com.digitalasset.canton.console.{
InstanceReference,
InstanceReferenceWithSequencerConnection,
LocalParticipantReference,
}
@ -69,8 +70,10 @@ object IntegrationTestUtilities {
GrabbedCounts(pcsCount, acceptedTransactionCount)
}
/** @param domainRef can either be a domain reference or a sequencer reference (in a distributed domain)
*/
def grabCounts(
domainRef: InstanceReferenceWithSequencerConnection,
domainRef: InstanceReference,
pr: LocalParticipantReference,
limit: Int = 100,
): GrabbedCounts = {

View File

@ -28,7 +28,7 @@ final case class TransactionFilter(filtersByParty: immutable.Map[Ref.Party, Filt
final case class Filters(inclusive: Option[InclusiveFilters]) {
def apply(identifier: Ref.Identifier): Boolean =
inclusive.fold(true)(_.templateIds.contains(identifier))
inclusive.fold(true)(_.templateFilters.exists(_.templateId == identifier))
}
object Filters {
@ -41,10 +41,16 @@ final case class InterfaceFilter(
interfaceId: Ref.Identifier,
includeView: Boolean,
includeCreateArgumentsBlob: Boolean,
includeCreateEventPayload: Boolean,
)
final case class TemplateFilter(
templateId: Ref.Identifier,
includeCreateEventPayload: Boolean,
)
final case class InclusiveFilters(
templateIds: immutable.Set[Ref.Identifier],
templateFilters: immutable.Set[TemplateFilter],
interfaceFilters: immutable.Set[InterfaceFilter],
)

View File

@ -11,7 +11,12 @@ import com.daml.lf.data.Ref.Party
import com.daml.lf.data.{Bytes, Ref, Time}
import com.daml.lf.value.Value.ContractId
import com.digitalasset.canton.ledger.api.domain
import com.digitalasset.canton.ledger.api.domain.{IdentityProviderId, JwksUrl, LedgerId}
import com.digitalasset.canton.ledger.api.domain.{
IdentityProviderId,
JwksUrl,
LedgerId,
TemplateFilter,
}
import com.digitalasset.canton.ledger.api.validation.ValidationErrors.*
import com.digitalasset.canton.ledger.error.groups.RequestValidationErrors
import com.digitalasset.canton.topology.DomainId
@ -301,12 +306,13 @@ object FieldValidator {
def validatedTemplateIdWithPackageIdResolutionFallback(
identifier: Identifier,
includeCreateEventPayload: Boolean,
resolveTemplateIds: Ref.QualifiedName => Either[StatusRuntimeException, Iterable[
Ref.Identifier
]],
)(upgradingEnabled: Boolean)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Iterable[Ref.Identifier]] =
): Either[StatusRuntimeException, Iterable[TemplateFilter]] =
for {
qualifiedName <- validateTemplateQualifiedName(identifier.moduleName, identifier.entityName)
templateIds <-
@ -314,7 +320,7 @@ object FieldValidator {
else
requirePackageId(identifier.packageId, "package_id")
.map(pkgId => Iterable(Ref.Identifier(pkgId, qualifiedName)))
} yield templateIds
} yield templateIds.map(TemplateFilter(_, includeCreateEventPayload))
def optionalString[T](s: String)(
someValidation: String => Either[StatusRuntimeException, T]

View File

@ -4,10 +4,15 @@
package com.digitalasset.canton.ledger.api.validation
import com.daml.error.ContextualizedErrorLogger
import com.daml.ledger.api.v1.transaction_filter.{Filters, InterfaceFilter, TransactionFilter}
import com.daml.ledger.api.v1.transaction_filter.{
Filters,
InclusiveFilters,
InterfaceFilter,
TemplateFilter,
TransactionFilter,
}
import com.daml.lf.data.Ref
import com.digitalasset.canton.ledger.api.domain
import com.digitalasset.canton.ledger.api.domain.InclusiveFilters
import io.grpc.StatusRuntimeException
import scalaz.std.either.*
import scalaz.std.list.*
@ -62,35 +67,86 @@ class TransactionFilterValidator(
.fold[Either[StatusRuntimeException, domain.Filters]](Right(domain.Filters.noFilter)) {
inclusive =>
for {
_ <- validatePresenceOfFilters(inclusive)
validatedIdents <-
inclusive.templateIds.toList
.traverse(
validatedTemplateIdWithPackageIdResolutionFallback(
_,
includeCreateEventPayload = false,
resolvePackageIds,
)(upgradingEnabled)
)
.map(_.flatten)
validatedTemplates <-
inclusive.templateFilters.toList
.traverse(validateTemplateFilter(_, resolvePackageIds, upgradingEnabled))
.map(_.flatten)
validatedInterfaces <-
inclusive.interfaceFilters.toList traverse validateInterfaceFilter
} yield domain.Filters(
Some(InclusiveFilters(validatedIdents.toSet, validatedInterfaces.toSet))
Some(
domain.InclusiveFilters(
(validatedIdents ++ validatedTemplates).toSet,
validatedInterfaces.toSet,
)
)
)
}
}
@annotation.nowarn(
"cat=deprecation&origin=com\\.daml\\.ledger\\.api\\.v1\\.transaction_filter\\.InclusiveFilters.*"
)
private def validatePresenceOfFilters(inclusive: InclusiveFilters)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Unit] = {
(inclusive.templateIds, inclusive.templateFilters) match {
case (_ :: _, _ :: _) =>
Left(invalidArgument("Either of `template_ids` or `template_filters` must be empty"))
case (_, _) => Right(())
}
}
private def validateTemplateFilter(
filter: TemplateFilter,
resolvePackageIds: Ref.QualifiedName => Either[StatusRuntimeException, Iterable[
Ref.Identifier
]],
upgradingEnabled: Boolean,
)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Iterable[domain.TemplateFilter]] = {
for {
templateId <- requirePresence(filter.templateId, "templateId")
validatedIds <- validatedTemplateIdWithPackageIdResolutionFallback(
templateId,
filter.includeCreateEventPayload,
resolvePackageIds,
)(upgradingEnabled)
} yield validatedIds
}
// Allow using deprecated Protobuf fields for backwards compatibility
@annotation.nowarn("cat=deprecation&origin=com\\.daml\\.ledger\\.api\\.v1\\.transaction_filter.*")
private def validateInterfaceFilter(filter: InterfaceFilter)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, domain.InterfaceFilter] = {
for {
_ <- Either.cond(
!filter.includeCreateArgumentsBlob || !filter.includeCreateEventPayload,
(),
invalidArgument(
"Either of `includeCreateArgumentsBlob` and `includeCreateEventPayload` must be false"
),
)
interfaceId <- requirePresence(filter.interfaceId, "interfaceId")
validatedId <- validateIdentifier(interfaceId)
} yield domain.InterfaceFilter(
interfaceId = validatedId,
includeView = filter.includeInterfaceView,
includeCreateArgumentsBlob = filter.includeCreateArgumentsBlob,
includeCreateEventPayload = filter.includeCreateEventPayload,
)
}
}

View File

@ -82,7 +82,8 @@ package object logging {
filters.filtersByParty.view.map { case (party, partyFilters) =>
party.toLoggingKey -> (partyFilters.inclusive match {
case None => LoggingValue.from("all-templates")
case Some(inclusiveFilters) => LoggingValue.from(inclusiveFilters.templateIds)
case Some(inclusiveFilters) =>
LoggingValue.from(inclusiveFilters.templateFilters.map(_.templateId))
})
}.toMap
)

View File

@ -581,7 +581,8 @@ object IndexServiceImpl {
.map(_.interfaceId)
.diff(metadata.interfaces)
.map(Right(_))
unknownTemplates = inclusiveFilter.templateIds
unknownTemplates = inclusiveFilter.templateFilters
.map(_.templateId)
.diff(metadata.templates.view.values.flatMap(_.all).toSet)
.map(Left(_))
unknownTemplateOrInterface <- unknownInterfaces ++ unknownTemplates
@ -697,7 +698,7 @@ object IndexServiceImpl {
.map(_.interfaceId)
.flatMap(metadata.interfacesImplementedBy.getOrElse(_, Set.empty))
.toSet
.++(inclusiveFilters.templateIds)
.++(inclusiveFilters.templateFilters.map(_.templateId))
private[index] def templateFilter(
metadata: PackageMetadata,

View File

@ -5,7 +5,7 @@ package com.digitalasset.canton.platform.store.dao
import com.daml.lf.data.Ref.{Identifier, Party}
import com.digitalasset.canton.ledger.api.domain
import com.digitalasset.canton.ledger.api.domain.{Filters, InterfaceFilter}
import com.digitalasset.canton.ledger.api.domain.{Filters, InterfaceFilter, TemplateFilter}
import com.digitalasset.canton.platform.store.dao.EventProjectionProperties.{
InterfaceViewFilter,
RenderResult,
@ -23,14 +23,20 @@ import com.digitalasset.canton.platform.store.dao.EventProjectionProperties.{
final case class EventProjectionProperties(
verbose: Boolean,
// Map(eventWitnessParty, Set(templateId))
witnessTemplateIdFilter: Map[String, Set[Identifier]] = Map.empty,
witnessTemplateIdFilter: Map[String, Set[TemplateFilter]] = Map.empty,
// Map(eventWitnessParty, Map(templateId -> Set(interfaceId)))
witnessInterfaceViewFilter: Map[String, Map[Identifier, InterfaceViewFilter]] = Map.empty,
) {
def render(witnesses: Set[String], templateId: Identifier): RenderResult = {
val renderContractArguments: Boolean = witnesses.view
def renderTemplate(filters: Set[TemplateFilter]) =
filters.isEmpty || filters.exists(_.templateId == templateId)
def renderPayload(filters: Set[TemplateFilter]) =
filters.exists(filter => filter.templateId == templateId && filter.includeCreateEventPayload)
val (renderContractArguments, renderCreateEventPayload): (Boolean, Boolean) = witnesses.view
.flatMap(witnessTemplateIdFilter.get)
.exists(templates => templates.isEmpty || templates(templateId))
.foldLeft((false, false))((acc, filters) =>
(acc._1 || renderTemplate(filters), acc._2 || renderPayload(filters))
)
val interfacesToRender: InterfaceViewFilter = witnesses.view
.flatMap(witnessInterfaceViewFilter.get(_).iterator)
@ -39,6 +45,7 @@ final case class EventProjectionProperties(
RenderResult(
interfacesToRender.contractArgumentsBlob,
renderContractArguments,
interfacesToRender.createEventPayload || renderCreateEventPayload,
interfacesToRender.interfaces,
)
}
@ -50,6 +57,7 @@ object EventProjectionProperties {
final case class InterfaceViewFilter(
interfaces: Set[Identifier],
contractArgumentsBlob: Boolean,
createEventPayload: Boolean,
) {
def append(
templateId: Identifier,
@ -59,17 +67,19 @@ object EventProjectionProperties {
InterfaceViewFilter(
interfaces ++ other.interfaces,
contractArgumentsBlob || other.contractArgumentsBlob,
createEventPayload || other.createEventPayload,
)
}
}
object InterfaceViewFilter {
val Empty = InterfaceViewFilter(Set.empty[Identifier], false)
val Empty = InterfaceViewFilter(Set.empty[Identifier], false, false)
}
final case class RenderResult(
contractArgumentsBlob: Boolean,
contractArguments: Boolean,
createEventPayoad: Boolean,
interfaces: Set[Identifier],
)
@ -98,21 +108,22 @@ object EventProjectionProperties {
private def witnessTemplateIdFilter(
domainTransactionFilter: domain.TransactionFilter,
alwaysPopulateArguments: Boolean,
): Map[String, Set[Identifier]] = {
): Map[String, Set[TemplateFilter]] = {
// TODO(#15076) Implement create event payloads for transaction trees
if (alwaysPopulateArguments)
domainTransactionFilter.filtersByParty.keysIterator
.map(_.toString -> Set.empty[Identifier])
.map(_.toString -> Set.empty[TemplateFilter])
.toMap
else
domainTransactionFilter.filtersByParty.iterator
.map { case (party, filters) => (party.toString, filters) }
.collect {
case (party, Filters(None)) => party -> Set.empty[Identifier]
case (party, Filters(None)) => party -> Set.empty[TemplateFilter]
case (party, Filters(Some(empty)))
if empty.templateIds.isEmpty && empty.interfaceFilters.isEmpty =>
party -> Set.empty[Identifier]
case (party, Filters(Some(nonEmptyFilter))) if nonEmptyFilter.templateIds.nonEmpty =>
party -> nonEmptyFilter.templateIds
if empty.templateFilters.isEmpty && empty.interfaceFilters.isEmpty =>
party -> Set.empty[TemplateFilter]
case (party, Filters(Some(nonEmptyFilter))) if nonEmptyFilter.templateFilters.nonEmpty =>
party -> nonEmptyFilter.templateFilters
}
.toMap
}
@ -144,6 +155,7 @@ object EventProjectionProperties {
InterfaceViewFilter(
interfaceFilters.filter(_.includeView).map(_.interfaceId),
interfaceFilters.exists(_.includeCreateArgumentsBlob),
interfaceFilters.exists(_.includeCreateEventPayload),
)
)
.toMap,

View File

@ -7,9 +7,10 @@ import com.daml.ledger.api.v1.event.CreatedEvent
import com.daml.ledger.api.v1.event_query_service.GetEventsByContractKeyResponse
import com.daml.ledger.api.v2.event_query_service.{Archived, Created, GetEventsByContractIdResponse}
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.{Identifier, Party}
import com.daml.lf.data.Ref.Party
import com.daml.lf.value.Value
import com.daml.lf.value.Value.ContractId
import com.digitalasset.canton.ledger.api.domain.TemplateFilter
import com.digitalasset.canton.logging.LoggingContextWithTrace
import com.digitalasset.canton.metrics.Metrics
import com.digitalasset.canton.platform
@ -45,7 +46,7 @@ private[dao] sealed class EventsReader(
// Used by LfEngineToApi
verbose = true,
// Needed to get create arguments mapped
witnessTemplateIdFilter = requestingParties.map(_ -> Set.empty[Identifier]).toMap,
witnessTemplateIdFilter = requestingParties.map(_ -> Set.empty[TemplateFilter]).toMap,
// We do not need interfaces mapped
witnessInterfaceViewFilter = Map.empty,
)
@ -100,7 +101,7 @@ private[dao] sealed class EventsReader(
// Used by LfEngineToApi
verbose = true,
// Needed to get create arguments mapped
witnessTemplateIdFilter = requestingParties.map(_ -> Set.empty[Identifier]).toMap,
witnessTemplateIdFilter = requestingParties.map(_ -> Set.empty[TemplateFilter]).toMap,
// We do not need interfaces mapped
witnessInterfaceViewFilter = Map.empty,
)

View File

@ -3,6 +3,7 @@
package com.digitalasset.canton.platform.store.dao.events
import cats.implicits.toTraverseOps
import com.daml.error.ContextualizedErrorLogger
import com.daml.ledger.api.v1.event.{CreatedEvent, ExercisedEvent, InterfaceView}
import com.daml.ledger.api.v1.value.{
@ -10,10 +11,12 @@ import com.daml.ledger.api.v1.value.{
Record as ApiRecord,
Value as ApiValue,
}
import com.daml.lf.data.Ref.Identifier
import com.daml.lf.data.Bytes
import com.daml.lf.data.Ref.{DottedName, Identifier, PackageId, Party}
import com.daml.lf.data.Time.Timestamp
import com.daml.lf.engine.{Engine, ValueEnricher}
import com.daml.lf.ledger.EventId
import com.daml.lf.transaction.Versioned
import com.daml.lf.transaction.{FatContractInstance, Node, TransactionCoder, Versioned}
import com.daml.lf.value.Value
import com.daml.lf.value.Value.VersionedValue
import com.daml.lf.engine as LfEngine
@ -28,6 +31,7 @@ import com.digitalasset.canton.metrics.Metrics
import com.digitalasset.canton.platform.apiserver.services.{ErrorCause, RejectionGenerators}
import com.digitalasset.canton.platform.packages.DeduplicatingPackageLoader
import com.digitalasset.canton.platform.participant.util.LfEngineToApi
import com.digitalasset.canton.platform.store.backend.EventStorageBackend.RawCreatedEvent
import com.digitalasset.canton.platform.store.dao.EventProjectionProperties
import com.digitalasset.canton.platform.store.dao.events.LfValueTranslation.ApiContractData
import com.digitalasset.canton.platform.store.serialization.{Compression, ValueSerializer}
@ -43,7 +47,10 @@ import com.digitalasset.canton.platform.{
QualifiedName as LfQualifiedName,
Value as LfValue,
}
import com.digitalasset.canton.serialization.ProtoConverter.InstantConverter
import com.google.protobuf
import com.google.protobuf.ByteString
import com.google.protobuf.timestamp.Timestamp as ApiTimestamp
import com.google.rpc.Status
import com.google.rpc.status.Status as ProtoStatus
import io.grpc.Status.Code
@ -255,6 +262,44 @@ final class LfValueTranslation(
@SuppressWarnings(Array("org.wartremover.warts.OptionPartial"))
lazy val templateId: LfIdentifier = apiIdentifierToDamlLfIdentifier(raw.partial.templateId.get)
@annotation.nowarn(
"cat=deprecation&origin=com\\.daml\\.ledger\\.api\\.v1\\.event\\.CreatedEvent.*"
)
def getFatContractInstance(createArgument: VersionedValue) = {
for {
contractId <- ContractId.fromString(raw.partial.contractId)
apiTemplateId <- raw.partial.templateId
.fold[Either[String, ApiIdentifier]](Left("missing templateId"))(Right(_))
packageId <- PackageId.fromString(apiTemplateId.moduleName)
moduleName <- DottedName.fromString(apiTemplateId.moduleName)
entityName <- DottedName.fromString(apiTemplateId.entityName)
templateId = Identifier(packageId, LfQualifiedName(moduleName, entityName))
signatories <- raw.partial.signatories.map(Party.fromString).sequence.map(_.toSet)
observers <- raw.partial.observers.map(Party.fromString).sequence.map(_.toSet)
apiCreatedAt <- raw.partial.metadata
.flatMap(_.createdAt)
.fold[Either[String, ApiTimestamp]](Left("missing createdAt"))(Right(_))
instant <- InstantConverter.fromProtoPrimitive(apiCreatedAt).left.map(_.message)
createdAt <- Timestamp.fromInstant(instant)
cantonData <- raw.partial.metadata
.map(_.driverMetadata)
.fold[Either[String, ByteString]](Left("missing cantonData"))(Right(_))
} yield FatContractInstance.fromCreateNode(
Node.Create(
coid = contractId,
templateId = templateId,
arg = createArgument.unversioned,
agreementText = raw.partial.agreementText.getOrElse(""),
signatories = signatories,
stakeholders = signatories ++ observers,
keyOpt = None, // add maintainers to the query returning data
version = createArgument.version,
),
createTime = createdAt,
cantonData = Bytes.fromByteString(cantonData),
)
}
for {
createKey <- Future(
raw.createKeyValue.map(decompressAndDeserialize(raw.createKeyValueCompression, _))
@ -268,12 +313,14 @@ final class LfValueTranslation(
templateId = templateId,
witnesses = raw.partial.witnessParties.toSet,
eventProjectionProperties = eventProjectionProperties,
fatContractInstance = getFatContractInstance(createArgument),
)
} yield raw.partial.copy(
createArguments = apiContractData.createArguments,
createArgumentsBlob = apiContractData.createArgumentsBlob,
contractKey = apiContractData.contractKey,
interfaceViews = apiContractData.interfaceViews,
createEventPayload = apiContractData.createEventPayload.getOrElse(ByteString.EMPTY),
)
}
@ -292,7 +339,7 @@ final class LfValueTranslation(
raw.exerciseResult.map(decompressAndDeserialize(raw.exerciseResultCompression, _))
@SuppressWarnings(Array("org.wartremover.warts.OptionPartial"))
lazy val temlateId: LfIdentifier = apiIdentifierToDamlLfIdentifier(raw.partial.templateId.get)
lazy val templateId: LfIdentifier = apiIdentifierToDamlLfIdentifier(raw.partial.templateId.get)
lazy val interfaceId: Option[LfIdentifier] =
raw.partial.interfaceId.map(apiIdentifierToDamlLfIdentifier)
lazy val choiceName: LfChoiceName = LfChoiceName.assertFromString(raw.partial.choice)
@ -305,7 +352,7 @@ final class LfValueTranslation(
verbose = verbose,
attribute = "exercise argument",
enrich = value =>
enricher.enrichChoiceArgument(temlateId, interfaceId, choiceName, value.unversioned),
enricher.enrichChoiceArgument(templateId, interfaceId, choiceName, value.unversioned),
)
exerciseResult <- exerciseResult match {
case Some(result) =>
@ -314,7 +361,7 @@ final class LfValueTranslation(
verbose = verbose,
attribute = "exercise result",
enrich = value =>
enricher.enrichChoiceResult(temlateId, interfaceId, choiceName, value.unversioned),
enricher.enrichChoiceResult(templateId, interfaceId, choiceName, value.unversioned),
).map(Some(_))
case None => Future.successful(None)
}
@ -327,6 +374,7 @@ final class LfValueTranslation(
}
def deserializeRaw(
createdEvent: RawCreatedEvent,
createArgument: Array[Byte],
createArgumentCompression: Compression.Algorithm,
createKeyValue: Option[Array[Byte]],
@ -338,6 +386,27 @@ final class LfValueTranslation(
ec: ExecutionContext,
loggingContext: LoggingContextWithTrace,
): Future[ApiContractData] = {
def getFatContractInstance(createArgument: VersionedValue) = {
for {
contractId <- ContractId.fromString(createdEvent.contractId)
signatories <- createdEvent.signatories.map(Party.fromString).toList.sequence.map(_.toSet)
observers <- createdEvent.observers.map(Party.fromString).toList.sequence.map(_.toSet)
} yield FatContractInstance.fromCreateNode(
Node.Create(
coid = contractId,
templateId = createdEvent.templateId,
arg = createArgument.unversioned,
agreementText = createdEvent.agreementText.getOrElse(""),
signatories = signatories,
stakeholders = signatories ++ observers,
keyOpt = None, // add maintainers to the query returning data
version = createArgument.version,
),
createTime = createdEvent.ledgerEffectiveTime,
cantonData = Bytes.fromByteArray(createdEvent.driverMetadata),
)
}
for {
createKey <- Future(
createKeyValue.map(decompressAndDeserialize(createKeyValueCompression, _))
@ -351,6 +420,7 @@ final class LfValueTranslation(
templateId = templateId,
witnesses = witnesses,
eventProjectionProperties = eventProjectionProperties,
fatContractInstance = getFatContractInstance(createArgument),
)
} yield apiContractData
}
@ -361,6 +431,7 @@ final class LfValueTranslation(
templateId: LfIdentifier,
witnesses: Set[String],
eventProjectionProperties: EventProjectionProperties,
fatContractInstance: => Either[String, FatContractInstance],
)(implicit
ec: ExecutionContext,
loggingContext: LoggingContextWithTrace,
@ -392,14 +463,26 @@ final class LfValueTranslation(
Future(ValueSerializer.serializeValueAny(value, "Cannot serialize contractArgumentsBlob"))
)
val asyncCreateEventPayload = condFuture(renderResult.createEventPayoad) {
(for {
fatInstance <- fatContractInstance
encoded <- TransactionCoder.encodeFatContractInstance(fatInstance).left.map(_.errorMessage)
} yield encoded).fold(
err => Future.failed(new RuntimeException(s"Cannot serialize createEventPayload: $err")),
Future.successful,
)
}
for {
contractArguments <- asyncContractArguments
contractArgumentsBlob <- asyncContractArgumentsBlob
createEventPayload <- asyncCreateEventPayload
contractKey <- asyncContractKey
interfaceViews <- asyncInterfaceViews
} yield ApiContractData(
createArguments = contractArguments,
createArgumentsBlob = contractArgumentsBlob,
createEventPayload = createEventPayload,
contractKey = contractKey,
interfaceViews = interfaceViews,
)
@ -526,6 +609,7 @@ object LfValueTranslation {
final case class ApiContractData(
createArguments: Option[ApiRecord],
createArgumentsBlob: Option[protobuf.any.Any],
createEventPayload: Option[ByteString],
contractKey: Option[ApiValue],
interfaceViews: Seq[InterfaceView],
)

View File

@ -21,9 +21,11 @@ import com.daml.ledger.api.v2.update_service.{
GetUpdateTreesResponse,
GetUpdatesResponse,
}
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.{Identifier, Party}
import com.daml.lf.data.{Bytes, Ref}
import com.daml.lf.transaction.{FatContractInstance, GlobalKeyWithMaintainers, Node}
import com.daml.lf.value.Value.ContractId
import com.digitalasset.canton.ledger.api.domain.TemplateFilter
import com.digitalasset.canton.logging.LoggingContextWithTrace
import com.digitalasset.canton.platform.api.v1.event.EventOps.TreeEventOps
import com.digitalasset.canton.platform.participant.util.LfEngineToApi
@ -139,7 +141,7 @@ private[events] object TransactionLogUpdatesConversions {
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter =
requestingParties.map(_ -> Set.empty[Ref.Identifier]).toMap,
requestingParties.map(_ -> Set.empty[TemplateFilter]).toMap,
),
lfValueTranslation = lfValueTranslation,
traceContext = traced.traceContext,
@ -284,7 +286,7 @@ private[events] object TransactionLogUpdatesConversions {
requestingParties,
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = requestingParties.map(_ -> Set.empty[Ref.Identifier]).toMap,
witnessTemplateIdFilter = requestingParties.map(_ -> Set.empty[TemplateFilter]).toMap,
),
lfValueTranslation = lfValueTranslation,
traceContext = traced.traceContext,
@ -497,6 +499,26 @@ private[events] object TransactionLogUpdatesConversions {
loggingContext: LoggingContextWithTrace,
executionContext: ExecutionContext,
): Future[apiEvent.CreatedEvent] = {
def getFatContractInstance = Right(
FatContractInstance.fromCreateNode(
Node.Create(
coid = createdEvent.contractId,
templateId = createdEvent.templateId,
arg = createdEvent.createArgument.unversioned,
agreementText = createdEvent.createAgreementText.getOrElse(""),
signatories = createdEvent.createSignatories,
stakeholders = createdEvent.createSignatories ++ createdEvent.createObservers,
keyOpt = createdEvent.createKey.flatMap(k =>
createdEvent.createKeyMaintainers.map(GlobalKeyWithMaintainers(k, _))
),
version = createdEvent.createArgument.version,
),
createTime = createdEvent.ledgerEffectiveTime,
cantonData = createdEvent.driverMetadata.getOrElse(Bytes.Empty),
)
)
lfValueTranslation
.toApiContractData(
value = createdEvent.createArgument,
@ -504,6 +526,7 @@ private[events] object TransactionLogUpdatesConversions {
templateId = createdEvent.templateId,
witnesses = requestingParties.view.filter(createdWitnesses(createdEvent)).toSet,
eventProjectionProperties = eventProjectionProperties,
fatContractInstance = getFatContractInstance,
)
.map(apiContractData =>
apiEvent.CreatedEvent(
@ -513,6 +536,7 @@ private[events] object TransactionLogUpdatesConversions {
contractKey = apiContractData.contractKey,
createArguments = apiContractData.createArguments,
createArgumentsBlob = apiContractData.createArgumentsBlob,
createEventPayload = apiContractData.createEventPayload.getOrElse(ByteString.EMPTY),
interfaceViews = apiContractData.interfaceViews,
witnessParties = requestingParties.view.filter(createdWitnesses(createdEvent)).toSeq,
signatories = createdEvent.createSignatories.toSeq,

View File

@ -19,6 +19,7 @@ import com.daml.ledger.api.v2.update_service.{
import com.daml.lf.data.Ref
import com.daml.lf.ledger.EventId
import com.daml.lf.transaction.NodeId
import com.digitalasset.canton.ledger.api.domain.TemplateFilter
import com.digitalasset.canton.ledger.offset.Offset
import com.digitalasset.canton.logging.LoggingContextWithTrace
import com.digitalasset.canton.metrics.Metrics
@ -35,7 +36,7 @@ import com.digitalasset.canton.platform.store.dao.{
LedgerDaoTransactionsReader,
}
import com.digitalasset.canton.platform.store.serialization.Compression
import com.digitalasset.canton.platform.{Identifier, Party, TemplatePartiesFilter}
import com.digitalasset.canton.platform.{Party, TemplatePartiesFilter}
import com.google.protobuf.ByteString
import io.opentelemetry.api.trace.Span
@ -100,7 +101,8 @@ private[dao] final class TransactionsReader(
requestingParties = requestingParties,
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = requestingParties.map(_.toString -> Set.empty[Identifier]).toMap,
witnessTemplateIdFilter =
requestingParties.map(_.toString -> Set.empty[TemplateFilter]).toMap,
),
)
}
@ -116,7 +118,8 @@ private[dao] final class TransactionsReader(
requestingParties = requestingParties,
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = requestingParties.map(_.toString -> Set.empty[Identifier]).toMap,
witnessTemplateIdFilter =
requestingParties.map(_.toString -> Set.empty[TemplateFilter]).toMap,
),
)
}
@ -282,6 +285,7 @@ private[dao] object TransactionsReader {
): Future[CreatedEvent] =
lfValueTranslation
.deserializeRaw(
createdEvent = rawCreatedEvent,
createArgument = rawCreatedEvent.createArgument,
createArgumentCompression = Compression.Algorithm
.assertLookup(rawCreatedEvent.createArgumentCompression),
@ -305,6 +309,7 @@ private[dao] object TransactionsReader {
contractKey = apiContractData.contractKey,
createArguments = apiContractData.createArguments,
createArgumentsBlob = apiContractData.createArgumentsBlob,
createEventPayload = apiContractData.createEventPayload.getOrElse(ByteString.EMPTY),
interfaceViews = apiContractData.interfaceViews,
witnessParties = rawCreatedEvent.witnessParties.toList,
signatories = rawCreatedEvent.signatories.toList,

View File

@ -7,7 +7,7 @@ import com.daml.grpc.GrpcStatus
import com.daml.lf.data.Ref
import com.daml.lf.value.Value.ContractId
import com.digitalasset.canton.ledger.api.domain
import com.digitalasset.canton.ledger.api.domain.InterfaceFilter
import com.digitalasset.canton.ledger.api.domain.{InterfaceFilter, TemplateFilter}
import com.digitalasset.canton.ledger.api.messages.transaction
import com.google.rpc.error_details
import io.grpc.Status.Code
@ -59,7 +59,8 @@ trait ValidatorTestUtils extends Matchers with Inside with OptionValues {
filters shouldEqual domain.Filters(
Some(
domain.InclusiveFilters(
templateIds = expectedTemplateIds,
templateFilters =
expectedTemplateIds.map(TemplateFilter(_, includeCreateEventPayload = false)),
interfaceFilters = Set(
InterfaceFilter(
interfaceId = Ref.Identifier(
@ -71,6 +72,7 @@ trait ValidatorTestUtils extends Matchers with Inside with OptionValues {
),
includeView = true,
includeCreateArgumentsBlob = true,
includeCreateEventPayload = false,
)
),
)

View File

@ -4,13 +4,14 @@
package com.digitalasset.canton.platform.index
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
import com.daml.lf.data.Ref.Identifier
import com.daml.lf.data.{Ref, Time}
import com.daml.lf.data.Ref.{Identifier, Party, QualifiedName}
import com.daml.lf.data.Time
import com.daml.nonempty.NonEmptyUtil
import com.digitalasset.canton.ledger.api.domain.{
Filters,
InclusiveFilters,
InterfaceFilter,
TemplateFilter,
TransactionFilter,
}
import com.digitalasset.canton.ledger.error.groups.RequestValidationErrors
@ -53,7 +54,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
val memoFunc = memoizedTransactionFilterProjection(
packageMetadataView = view,
transactionFilter = TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set(), Set(InterfaceFilter(iface1, true, true)))))
Map(party -> Filters(InclusiveFilters(Set(), Set(iface1Filter))))
),
verbose = true,
alwaysPopulateArguments = false,
@ -69,8 +70,8 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
TemplatePartiesFilter(Map(template1 -> Set(party)), Set()),
EventProjectionProperties(
true,
Map.empty[String, Set[Identifier]],
Map(party.toString -> Map(template1 -> InterfaceViewFilter(Set(iface1), true))),
Map.empty[String, Set[TemplateFilter]],
Map(party.toString -> Map(template1 -> InterfaceViewFilter(Set(iface1), true, false))),
),
)
) // filter gets complicated, filters template1 for iface1, projects iface1
@ -91,11 +92,11 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
),
EventProjectionProperties(
true,
Map.empty[String, Set[Identifier]],
Map.empty[String, Set[TemplateFilter]],
Map(
party.toString -> Map(
template1 -> InterfaceViewFilter(Set(iface1), true),
template2 -> InterfaceViewFilter(Set(iface1), true),
template1 -> InterfaceViewFilter(Set(iface1), true, false),
template2 -> InterfaceViewFilter(Set(iface1), true, false),
)
),
),
@ -110,7 +111,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
val memoFunc = memoizedTransactionFilterProjection(
packageMetadataView = view,
transactionFilter = TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set(), Set(InterfaceFilter(iface1, true, true)))))
Map(party -> Filters(InclusiveFilters(Set(), Set(iface1Filter))))
),
verbose = true,
alwaysPopulateArguments = true,
@ -121,7 +122,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
EventProjectionProperties(
true,
Map(party -> Set.empty),
Map(party.toString -> Map(template1 -> InterfaceViewFilter(Set(iface1), true))),
Map(party.toString -> Map(template1 -> InterfaceViewFilter(Set(iface1), true, false))),
),
)
)
@ -159,7 +160,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
Map(
party -> Filters(None),
party2 -> Filters(
Some(InclusiveFilters(templateIds = Set(template1), interfaceFilters = Set()))
Some(InclusiveFilters(templateFilters = Set(template1Filter), interfaceFilters = Set()))
),
)
)
@ -205,7 +206,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
Map(
party -> Filters(None),
party2 -> Filters(
Some(InclusiveFilters(templateIds = Set(template1), interfaceFilters = Set()))
Some(InclusiveFilters(templateFilters = Set(template1Filter), interfaceFilters = Set()))
),
)
),
@ -224,7 +225,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
it should "provide a template filter for a simple template filter" in new Scope {
templateFilter(
PackageMetadata(),
TransactionFilter(Map(party -> Filters(InclusiveFilters(Set(template1), Set())))),
TransactionFilter(Map(party -> Filters(InclusiveFilters(Set(template1Filter), Set())))),
) shouldBe Map(template1 -> Set(party))
}
@ -232,7 +233,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
templateFilter(
PackageMetadata(),
TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set(), Set(InterfaceFilter(iface1, true, true)))))
Map(party -> Filters(InclusiveFilters(Set(), Set(iface1Filter))))
),
) shouldBe Map.empty
}
@ -241,7 +242,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
templateFilter(
PackageMetadata(interfacesImplementedBy = Map(iface1 -> Set(template1))),
TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set(), Set(InterfaceFilter(iface1, true, true)))))
Map(party -> Filters(InclusiveFilters(Set(), Set(iface1Filter))))
),
) shouldBe Map(template1 -> Set(party))
}
@ -252,7 +253,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
TransactionFilter(
Map(
party -> Filters(
InclusiveFilters(Set(template1), Set(InterfaceFilter(iface1, true, true)))
InclusiveFilters(Set(template1Filter), Set(iface1Filter))
)
)
),
@ -268,10 +269,10 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
Map(
party -> Filters(
InclusiveFilters(
templateIds = Set(template3),
templateFilters = Set(TemplateFilter(template3, false)),
interfaceFilters = Set(
InterfaceFilter(iface1, true, true),
InterfaceFilter(iface2, true, true),
iface1Filter,
iface2Filter,
),
)
)
@ -295,7 +296,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
it should "return an unknown template for not known template" in new Scope {
checkUnknownTemplatesOrInterfaces(
new TransactionFilter(Map(party -> Filters(InclusiveFilters(Set(template1), Set())))),
new TransactionFilter(Map(party -> Filters(InclusiveFilters(Set(template1Filter), Set())))),
PackageMetadata(),
) shouldBe List(Left(template1))
}
@ -303,7 +304,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
it should "return an unknown interface for not known interface" in new Scope {
checkUnknownTemplatesOrInterfaces(
new TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set(), Set(InterfaceFilter(iface1, true, true)))))
Map(party -> Filters(InclusiveFilters(Set(), Set(iface1Filter))))
),
PackageMetadata(),
) shouldBe List(Right(iface1))
@ -312,7 +313,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
it should "return zero unknown interfaces for known interface" in new Scope {
checkUnknownTemplatesOrInterfaces(
new TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set(), Set(InterfaceFilter(iface1, true, true)))))
Map(party -> Filters(InclusiveFilters(Set(), Set(iface1Filter))))
),
PackageMetadata(interfaces = Set(iface1)),
) shouldBe List()
@ -321,7 +322,7 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
it should "return zero unknown templates for known templates" in new Scope {
checkUnknownTemplatesOrInterfaces(
new TransactionFilter(Map(party -> Filters(InclusiveFilters(Set(template1), Set())))),
new TransactionFilter(Map(party -> Filters(InclusiveFilters(Set(template1Filter), Set())))),
PackageMetadata(templates = Map(templatesForQn1)),
) shouldBe List()
}
@ -331,10 +332,10 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
new TransactionFilter(
Map(
party -> Filters(
InclusiveFilters(Set(template1), Set(InterfaceFilter(iface1, true, true)))
InclusiveFilters(Set(template1Filter), Set(iface1Filter))
),
party2 -> Filters(
InclusiveFilters(Set(template2, template3), Set(InterfaceFilter(iface2, true, true)))
InclusiveFilters(Set(template2Filter, template3Filter), Set(iface2Filter))
),
)
),
@ -381,19 +382,38 @@ class IndexServiceImplSpec extends AnyFlatSpec with Matchers with MockitoSugar {
object IndexServiceImplSpec {
trait Scope extends MockitoSugar {
val party = Ref.Party.assertFromString("party")
val party2 = Ref.Party.assertFromString("party2")
val templateQualifiedName1 = Ref.QualifiedName.assertFromString("ModuleName:template1")
val template1 = Ref.Identifier.assertFromString("PackageName:ModuleName:template1")
val templatesForQn1 = templateQualifiedName1 ->
val party: Party = Party.assertFromString("party")
val party2: Party = Party.assertFromString("party2")
val templateQualifiedName1: QualifiedName =
QualifiedName.assertFromString("ModuleName:template1")
val template1: Identifier = Identifier.assertFromString("PackageName:ModuleName:template1")
val template1Filter: TemplateFilter =
TemplateFilter(templateId = template1, includeCreateEventPayload = false)
val templatesForQn1: (QualifiedName, TemplatesForQualifiedName) = templateQualifiedName1 ->
TemplatesForQualifiedName(
NonEmptyUtil.fromUnsafe(Set(template1)),
TemplateIdWithPriority(template1, Time.Timestamp.Epoch),
)
val template2 = Ref.Identifier.assertFromString("PackageName:ModuleName:template2")
val template3 = Ref.Identifier.assertFromString("PackageName:ModuleName:template3")
val iface1 = Ref.Identifier.assertFromString("PackageName:ModuleName:iface1")
val iface2 = Ref.Identifier.assertFromString("PackageName:ModuleName:iface2")
val view = mock[PackageMetadataView]
val template2: Identifier = Identifier.assertFromString("PackageName:ModuleName:template2")
val template2Filter: TemplateFilter =
TemplateFilter(templateId = template2, includeCreateEventPayload = false)
val template3: Identifier = Identifier.assertFromString("PackageName:ModuleName:template3")
val template3Filter: TemplateFilter =
TemplateFilter(templateId = template3, includeCreateEventPayload = false)
val iface1: Identifier = Identifier.assertFromString("PackageName:ModuleName:iface1")
val iface1Filter: InterfaceFilter = InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = true,
includeCreateEventPayload = false,
)
val iface2: Identifier = Identifier.assertFromString("PackageName:ModuleName:iface2")
val iface2Filter: InterfaceFilter = InterfaceFilter(
iface2,
includeView = true,
includeCreateArgumentsBlob = true,
includeCreateEventPayload = false,
)
val view: PackageMetadataView = mock[PackageMetadataView]
}
}

View File

@ -3,12 +3,12 @@
package com.digitalasset.canton.platform.store.dao
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.Identifier
import com.daml.lf.data.Ref.{Identifier, Party}
import com.digitalasset.canton.ledger.api.domain.{
Filters,
InclusiveFilters,
InterfaceFilter,
TemplateFilter,
TransactionFilter,
}
import com.digitalasset.canton.platform.store.dao.EventProjectionProperties.RenderResult
@ -26,12 +26,12 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
it should "project nothing in case of empty filters" in new Scope {
EventProjectionProperties(noFilter, true, noInterface, false)
.render(Set.empty, id) shouldBe RenderResult(false, false, Set.empty)
.render(Set.empty, id) shouldBe RenderResult(false, false, false, Set.empty)
}
it should "project nothing in case of irrelevant filters" in new Scope {
EventProjectionProperties(wildcardFilter, true, interfaceImpl, false)
.render(Set.empty, id) shouldBe RenderResult(false, false, Set.empty)
.render(Set.empty, id) shouldBe RenderResult(false, false, false, Set.empty)
}
it should "project contract arguments in case of match by template" in new Scope {
@ -41,21 +41,21 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
EventProjectionProperties(transactionFilter, true, noInterface, false).render(
Set(party),
template1,
) shouldBe RenderResult(false, true, Set.empty)
) shouldBe RenderResult(false, true, false, Set.empty)
}
it should "project contract arguments in case of wildcard match" in new Scope {
EventProjectionProperties(wildcardFilter, true, noInterface, false).render(
Set(party),
template1,
) shouldBe RenderResult(false, true, Set.empty)
) shouldBe RenderResult(false, true, false, Set.empty)
}
it should "project contract arguments in case of empty InclusiveFilters" in new Scope {
EventProjectionProperties(emptyInclusiveFilters, true, noInterface, false).render(
Set(party),
template1,
) shouldBe RenderResult(false, true, Set.empty)
) shouldBe RenderResult(false, true, false, Set.empty)
}
it should "project contract arguments with wildcard and another filter" in new Scope {
@ -63,7 +63,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
new TransactionFilter(
Map(
party -> Filters(Some(InclusiveFilters(Set.empty, Set.empty))),
party2 -> Filters(Some(InclusiveFilters(Set(template1), Set.empty))),
party2 -> Filters(Some(InclusiveFilters(Set(template1Filter), Set.empty))),
)
),
true,
@ -72,7 +72,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
).render(
Set(party, party2),
template2,
) shouldBe RenderResult(false, true, Set.empty)
) shouldBe RenderResult(false, true, false, Set.empty)
}
it should "do not project contract arguments with wildcard and another filter, if queried non wildcard party/template combination" in new Scope {
@ -80,7 +80,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
new TransactionFilter(
Map(
party -> Filters(Some(InclusiveFilters(Set.empty, Set.empty))),
party2 -> Filters(Some(InclusiveFilters(Set(template1), Set.empty))),
party2 -> Filters(Some(InclusiveFilters(Set(template1Filter), Set.empty))),
)
),
true,
@ -89,7 +89,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
).render(
Set(party2),
template2,
) shouldBe RenderResult(false, false, Set.empty)
) shouldBe RenderResult(false, false, false, Set.empty)
}
it should "project contract arguments with wildcard and another filter with alwaysPopulateArguments, if queried non wildcard party/template combination" in new Scope {
@ -97,7 +97,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
new TransactionFilter(
Map(
party -> Filters(Some(InclusiveFilters(Set.empty, Set.empty))),
party2 -> Filters(Some(InclusiveFilters(Set(template1), Set.empty))),
party2 -> Filters(Some(InclusiveFilters(Set(template1Filter), Set.empty))),
)
),
true,
@ -106,7 +106,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
).render(
Set(party2),
template2,
) shouldBe RenderResult(false, true, Set.empty)
) shouldBe RenderResult(false, true, false, Set.empty)
}
it should "project interface in case of match by interface id and witness" in new Scope {
@ -114,13 +114,20 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
Some(
InclusiveFilters(
Set.empty,
Set(InterfaceFilter(iface1, includeView = true, includeCreateArgumentsBlob = false)),
Set(
InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
)
),
)
)
)
val transactionFilter = new TransactionFilter(Map(party -> filter))
EventProjectionProperties(transactionFilter, true, interfaceImpl, false)
.render(Set(party), template1) shouldBe RenderResult(false, false, Set(iface1))
.render(Set(party), template1) shouldBe RenderResult(false, false, false, Set(iface1))
}
it should "project interface in case of match by interface id and witness with alwaysPopulateArguments" in new Scope {
@ -128,13 +135,20 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
Some(
InclusiveFilters(
Set.empty,
Set(InterfaceFilter(iface1, includeView = true, includeCreateArgumentsBlob = false)),
Set(
InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
)
),
)
)
)
val transactionFilter = new TransactionFilter(Map(party -> filter))
EventProjectionProperties(transactionFilter, true, interfaceImpl, true)
.render(Set(party), template1) shouldBe RenderResult(false, true, Set(iface1))
.render(Set(party), template1) shouldBe RenderResult(false, true, false, Set(iface1))
}
it should "not project interface in case of match by interface id and witness" in new Scope {
@ -142,22 +156,36 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
Some(
InclusiveFilters(
Set.empty,
Set(InterfaceFilter(iface1, includeView = false, includeCreateArgumentsBlob = false)),
Set(
InterfaceFilter(
iface1,
includeView = false,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
)
),
)
)
)
val transactionFilter = new TransactionFilter(Map(party -> filter))
EventProjectionProperties(transactionFilter, true, interfaceImpl, false)
.render(Set(party), template1) shouldBe RenderResult(false, false, Set.empty)
.render(Set(party), template1) shouldBe RenderResult(false, false, false, Set.empty)
}
it should "project an interface and template in case of match by interface id, template and witness" in new Scope {
val filter = Filters(
Some(
InclusiveFilters(
Set(template1),
Set(InterfaceFilter(iface1, includeView = true, includeCreateArgumentsBlob = false)),
Set(template1Filter),
Set(
InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
)
),
)
)
)
@ -167,15 +195,22 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
)
)
EventProjectionProperties(transactionFilter, true, interfaceImpl, false)
.render(Set(party), template1) shouldBe RenderResult(false, true, Set(iface1))
.render(Set(party), template1) shouldBe RenderResult(false, true, false, Set(iface1))
}
it should "project an interface and template in case of match by interface id, template and witness with alwaysPopulateArguments" in new Scope {
val filter = Filters(
Some(
InclusiveFilters(
Set(template1),
Set(InterfaceFilter(iface1, includeView = true, includeCreateArgumentsBlob = false)),
Set(template1Filter),
Set(
InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
)
),
)
)
)
@ -185,7 +220,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
)
)
EventProjectionProperties(transactionFilter, true, interfaceImpl, true)
.render(Set(party), template1) shouldBe RenderResult(false, true, Set(iface1))
.render(Set(party), template1) shouldBe RenderResult(false, true, false, Set(iface1))
}
it should "project multiple interfaces in case of match by multiple interface ids and witness" in new Scope {
@ -194,15 +229,25 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
InclusiveFilters(
Set.empty,
Set(
InterfaceFilter(iface1, includeView = true, includeCreateArgumentsBlob = false),
InterfaceFilter(iface2, includeView = true, includeCreateArgumentsBlob = false),
InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
InterfaceFilter(
iface2,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
),
)
)
)
val transactionFilter = new TransactionFilter(Map(party -> filter))
EventProjectionProperties(transactionFilter, true, interfaceImpl, false)
.render(Set(party), template1) shouldBe RenderResult(false, false, Set(iface1, iface2))
.render(Set(party), template1) shouldBe RenderResult(false, false, false, Set(iface1, iface2))
}
it should "deduplicate projected interfaces and include the view" in new Scope {
@ -213,8 +258,18 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
InclusiveFilters(
Set.empty,
Set(
InterfaceFilter(iface1, includeView = false, includeCreateArgumentsBlob = false),
InterfaceFilter(iface2, includeView = true, includeCreateArgumentsBlob = false),
InterfaceFilter(
iface1,
includeView = false,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
InterfaceFilter(
iface2,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
),
)
)
@ -224,8 +279,18 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
InclusiveFilters(
Set.empty,
Set(
InterfaceFilter(iface1, includeView = true, includeCreateArgumentsBlob = false),
InterfaceFilter(iface2, includeView = true, includeCreateArgumentsBlob = false),
InterfaceFilter(
iface1,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
InterfaceFilter(
iface2,
includeView = true,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
),
)
)
@ -236,46 +301,51 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
.render(Set(party, party2), template1) shouldBe RenderResult(
false,
false,
false,
Set(iface2, iface1),
)
}
it should "project contract arguments blob in case of match by interface" in new Scope {
val transactionFilter = new TransactionFilter(
Map(party -> Filters(InclusiveFilters(Set.empty, Set(InterfaceFilter(iface1, false, true)))))
Map(
party -> Filters(
InclusiveFilters(Set.empty, Set(InterfaceFilter(iface1, false, true, false)))
)
)
)
EventProjectionProperties(transactionFilter, true, interfaceImpl, false).render(
Set(party),
template1,
) shouldBe RenderResult(true, false, Set.empty)
) shouldBe RenderResult(true, false, false, Set.empty)
}
it should "project contract arguments blob in case of match by interface and template" in new Scope {
val transactionFilter = new TransactionFilter(
Map(
party -> Filters(
InclusiveFilters(Set(template1), Set(InterfaceFilter(iface1, false, true)))
InclusiveFilters(Set(template1Filter), Set(InterfaceFilter(iface1, false, true, false)))
)
)
)
EventProjectionProperties(transactionFilter, true, interfaceImpl, false).render(
Set(party),
template1,
) shouldBe RenderResult(true, true, Set.empty)
) shouldBe RenderResult(true, true, false, Set.empty)
}
it should "project contract arguments blob in case of match by interface and template with include the view" in new Scope {
val transactionFilter = new TransactionFilter(
Map(
party -> Filters(
InclusiveFilters(Set(template1), Set(InterfaceFilter(iface1, true, true)))
InclusiveFilters(Set(template1Filter), Set(InterfaceFilter(iface1, true, true, false)))
)
)
)
EventProjectionProperties(transactionFilter, true, interfaceImpl, false).render(
Set(party),
template1,
) shouldBe RenderResult(true, true, Set(iface1))
) shouldBe RenderResult(true, true, false, Set(iface1))
}
it should "project contract arguments blob in case of at least a single interface requesting it" in new Scope {
@ -285,8 +355,18 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
InclusiveFilters(
Set.empty,
Set(
InterfaceFilter(iface1, false, includeCreateArgumentsBlob = true),
InterfaceFilter(iface2, false, includeCreateArgumentsBlob = false),
InterfaceFilter(
iface1,
false,
includeCreateArgumentsBlob = true,
includeCreateEventPayload = false,
),
InterfaceFilter(
iface2,
false,
includeCreateArgumentsBlob = false,
includeCreateEventPayload = false,
),
),
)
)
@ -295,7 +375,7 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
EventProjectionProperties(transactionFilter, true, interfaceImpl, false).render(
Set(party),
template1,
) shouldBe RenderResult(true, false, Set.empty)
) shouldBe RenderResult(true, false, false, Set.empty)
}
it should "not project contract arguments blob in case of no match by interface" in new Scope {
@ -305,7 +385,12 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
InclusiveFilters(
Set.empty,
Set(
InterfaceFilter(iface1, false, includeCreateArgumentsBlob = true)
InterfaceFilter(
iface1,
false,
includeCreateArgumentsBlob = true,
includeCreateEventPayload = false,
)
),
)
)
@ -314,33 +399,35 @@ class EventProjectionPropertiesSpec extends AnyFlatSpec with Matchers {
EventProjectionProperties(transactionFilter, true, interfaceImpl, false).render(
Set(party),
template2,
) shouldBe RenderResult(false, false, Set.empty)
) shouldBe RenderResult(false, false, false, Set.empty)
}
}
object EventProjectionPropertiesSpec {
trait Scope {
val template1 = Ref.Identifier.assertFromString("PackageName:ModuleName:template1")
val template2 = Ref.Identifier.assertFromString("PackageName:ModuleName:template2")
val id = Ref.Identifier.assertFromString("PackageName:ModuleName:id")
val iface1 = Ref.Identifier.assertFromString("PackageName:ModuleName:iface1")
val iface2 = Ref.Identifier.assertFromString("PackageName:ModuleName:iface2")
val template1: Identifier = Identifier.assertFromString("PackageName:ModuleName:template1")
val template1Filter: TemplateFilter =
TemplateFilter(templateId = template1, includeCreateEventPayload = false)
val template2: Identifier = Identifier.assertFromString("PackageName:ModuleName:template2")
val id: Identifier = Identifier.assertFromString("PackageName:ModuleName:id")
val iface1: Identifier = Identifier.assertFromString("PackageName:ModuleName:iface1")
val iface2: Identifier = Identifier.assertFromString("PackageName:ModuleName:iface2")
val noInterface: Identifier => Set[Ref.Identifier] = _ => Set.empty[Ref.Identifier]
val interfaceImpl: Identifier => Set[Ref.Identifier] = {
val noInterface: Identifier => Set[Identifier] = _ => Set.empty[Identifier]
val interfaceImpl: Identifier => Set[Identifier] = {
case `iface1` => Set(template1)
case `iface2` => Set(template1, template2)
case _ => Set.empty
}
val party = Ref.Party.assertFromString("party")
val party2 = Ref.Party.assertFromString("party2")
val party: Party = Party.assertFromString("party")
val party2: Party = Party.assertFromString("party2")
val noFilter = new TransactionFilter(Map())
val wildcardFilter = new TransactionFilter(Map(party -> Filters(None)))
val emptyInclusiveFilters = new TransactionFilter(
Map(party -> Filters(Some(InclusiveFilters(Set.empty, Set.empty))))
)
def templateFilterFor(templateId: Ref.Identifier): Option[InclusiveFilters] = Some(
InclusiveFilters(Set(templateId), Set.empty)
def templateFilterFor(templateId: Identifier): Option[InclusiveFilters] = Some(
InclusiveFilters(Set(TemplateFilter(templateId, false)), Set.empty)
)
}
}

View File

@ -8,6 +8,7 @@ import akka.stream.scaladsl.{Sink, Source}
import com.daml.ledger.api.v1.event.CreatedEvent
import com.daml.ledger.api.v2.state_service.GetActiveContractsResponse
import com.daml.lf.data.Ref.{Identifier, Party}
import com.digitalasset.canton.ledger.api.domain.TemplateFilter
import com.digitalasset.canton.platform.TemplatePartiesFilter
import com.digitalasset.canton.platform.participant.util.LfEngineToApi
import org.scalatest.*
@ -136,7 +137,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
filter = TemplatePartiesFilter(Map(otherTemplateId -> Set(party1)), Set.empty),
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(party1 -> Set(otherTemplateId)),
witnessTemplateIdFilter = Map(party1 -> Set(otherTemplateIdFilter)),
),
multiDomainEnabled = false,
)
@ -176,8 +177,8 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
party1 -> Set(otherTemplateId),
party2 -> Set(otherTemplateId),
party1 -> Set(otherTemplateIdFilter),
party2 -> Set(otherTemplateIdFilter),
),
),
multiDomainEnabled = false,
@ -226,8 +227,8 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
party1 -> Set(someTemplateId),
party2 -> Set(otherTemplateId),
party1 -> Set(someTemplateIdFilter),
party2 -> Set(otherTemplateIdFilter),
),
),
multiDomainEnabled = false,
@ -275,7 +276,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
Map(
party1 -> Set(someTemplateId),
party1 -> Set(someTemplateIdFilter),
party2 -> Set.empty,
),
),
@ -304,6 +305,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
// affect the results
val unknownParty = Party.assertFromString(UUID.randomUUID.toString)
val unknownTemplate = Identifier.assertFromString("pkg:Mod:Template")
val unknownTemplateFilter = TemplateFilter(unknownTemplate, includeCreateEventPayload = false)
for {
_ <- store(
@ -330,7 +332,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
party1 -> Set(someTemplateId),
party1 -> Set(someTemplateIdFilter),
party2 -> Set.empty,
),
),
@ -350,7 +352,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
party1 -> Set(someTemplateId),
party1 -> Set(someTemplateIdFilter),
party2 -> Set.empty,
unknownParty -> Set.empty,
),
@ -372,7 +374,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
party1 -> Set(someTemplateId, unknownTemplate),
party1 -> Set(someTemplateIdFilter, unknownTemplateFilter),
party2 -> Set.empty,
),
),
@ -393,7 +395,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
party1 -> Set(someTemplateId, unknownTemplate),
party1 -> Set(someTemplateIdFilter, unknownTemplateFilter),
party2 -> Set.empty,
unknownParty -> Set.empty,
),
@ -414,7 +416,7 @@ private[dao] trait JdbcLedgerDaoActiveContractsSpec
eventProjectionProperties = EventProjectionProperties(
verbose = true,
witnessTemplateIdFilter = Map(
unknownParty -> Set(unknownTemplate)
unknownParty -> Set(unknownTemplateFilter)
),
),
multiDomainEnabled = false,

View File

@ -14,6 +14,7 @@ import com.daml.lf.transaction.*
import com.daml.lf.transaction.test.{NodeIdTransactionBuilder, TransactionBuilder}
import com.daml.lf.value.Value.{ContractId, ContractInstance, ValueText, VersionedContractInstance}
import com.daml.lf.value.Value as LfValue
import com.digitalasset.canton.ledger.api.domain.TemplateFilter
import com.digitalasset.canton.ledger.configuration.{Configuration, LedgerTimeModel}
import com.digitalasset.canton.ledger.offset.Offset
import com.digitalasset.canton.ledger.participant.state.index.v2
@ -93,6 +94,8 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend with OptionVa
Some(Ref.Name.assertFromString(name))
protected final val someTemplateId = testIdentifier("ParameterShowcase")
protected final val someTemplateIdFilter =
TemplateFilter(someTemplateId, includeCreateEventPayload = false)
protected final val someValueText = LfValue.ValueText("some text")
protected final val someValueInt = LfValue.ValueInt64(1)
protected final val someValueNumeric =
@ -154,6 +157,8 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend with OptionVa
protected final val someVersionedContractInstance = Versioned(txVersion, someContractInstance)
protected final val otherTemplateId = testIdentifier("Dummy")
protected final val otherTemplateIdFilter =
TemplateFilter(otherTemplateId, includeCreateEventPayload = false)
protected final val otherContractArgument = LfValue.ValueRecord(
Some(otherTemplateId),
ImmArray(

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
build-options:
- --target=1.14
name: JsonEncodingTest

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
name: AdminWorkflows
source: AdminWorkflows.daml
version: 2.8.0

View File

@ -1,4 +1,4 @@
sdk-version: 2.8.0-snapshot.20231023.12243.0.v14f9f58d
sdk-version: 2.8.0-snapshot.20231026.12262.0.vb12eb2ad
build-options:
- --target=1.14
name: AdminWorkflowsWithVacuuming

View File

@ -14,6 +14,7 @@ import com.daml.ledger.javaapi.data.{
ExercisedEvent,
Transaction as JavaTransaction,
TransactionTree,
TreeEvent,
}
import scala.jdk.CollectionConverters.*
@ -48,7 +49,7 @@ object JavaDecodeUtil {
companion: ContractCompanion[?, TCid, ?]
)(event: ExercisedEvent): Option[TCid] =
Option.when(event.getTemplateId == companion.TEMPLATE_ID && event.isConsuming)(
companion.toContractId(new ContractId((event.getContractId)))
companion.toContractId(new ContractId(event.getContractId))
)
def treeToCreated(transaction: TransactionTree): Seq[JavaCreatedEvent] =
@ -67,4 +68,19 @@ object JavaDecodeUtil {
created <- treeToCreated(transaction)
a <- decodeCreated(companion)(created).toList
} yield a
def decodeAllArchivedTree[TCid](
companion: ContractCompanion[?, TCid, ?]
)(transaction: TransactionTree): Seq[TCid] =
decodeAllArchivedTreeFromTreeEvents(companion)(transaction.getEventsById.asScala.toMap)
def decodeAllArchivedTreeFromTreeEvents[TCid](
companion: ContractCompanion[?, TCid, ?]
)(eventsById: Map[String, TreeEvent]): Seq[TCid] =
for {
event <- eventsById.values.toList
archive = event.toProtoTreeEvent.getExercised
if archive.getConsuming && archive.getTemplateId == companion.TEMPLATE_ID.toProto
} yield companion.toContractId(new ContractId(archive.getContractId))
}

View File

@ -9,7 +9,7 @@ import cats.syntax.functor.*
import com.daml.nameof.NameOf.functionFullName
import com.digitalasset.canton.crypto.{DomainSnapshotSyncCryptoApi, DomainSyncCryptoClient}
import com.digitalasset.canton.data.CantonTimestamp
import com.digitalasset.canton.lifecycle.{FlagCloseable, FutureUnlessShutdown, HasCloseContext}
import com.digitalasset.canton.lifecycle.{FlagCloseable, FutureUnlessShutdown}
import com.digitalasset.canton.logging.NamedLogging
import com.digitalasset.canton.participant.protocol.RequestJournal.RequestState
import com.digitalasset.canton.participant.protocol.conflictdetection.ActivenessSet
@ -42,8 +42,7 @@ abstract class AbstractMessageProcessor(
protocolVersion: ProtocolVersion,
)(implicit ec: ExecutionContext)
extends NamedLogging
with FlagCloseable
with HasCloseContext {
with FlagCloseable {
protected def terminateRequest(
requestCounter: RequestCounter,
@ -191,10 +190,10 @@ abstract class AbstractMessageProcessor(
if (!isCleanReplay(requestCounter)) {
val timeoutF =
requestFutures.timeoutResult.flatMap { timeoutResult =>
if (timeoutResult.timedOut) FutureUnlessShutdown.outcomeF(onTimeout)
else FutureUnlessShutdown.unit
if (timeoutResult.timedOut) onTimeout
else Future.unit
}
FutureUtil.doNotAwaitUnlessShutdown(timeoutF, "Handling timeout failed")
FutureUtil.doNotAwait(timeoutF, "Handling timeout failed")
}
} yield ()

View File

@ -3,15 +3,9 @@
package com.digitalasset.canton.participant.protocol
import com.digitalasset.canton.concurrent.FutureSupervisor
import com.digitalasset.canton.config.ProcessingTimeout
import com.digitalasset.canton.RequestCounter
import com.digitalasset.canton.concurrent.{FutureSupervisor, SupervisedPromise}
import com.digitalasset.canton.data.{CantonTimestamp, ConcurrentHMap}
import com.digitalasset.canton.lifecycle.{
FlagCloseable,
FutureUnlessShutdown,
HasCloseContext,
PromiseUnlessShutdown,
}
import com.digitalasset.canton.logging.{NamedLoggerFactory, NamedLogging}
import com.digitalasset.canton.participant.protocol.Phase37Synchronizer.*
import com.digitalasset.canton.participant.protocol.ProcessingSteps.{
@ -22,11 +16,9 @@ import com.digitalasset.canton.participant.protocol.ProtocolProcessor.PendingReq
import com.digitalasset.canton.protocol.RequestId
import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.util.ErrorUtil
import com.digitalasset.canton.{DiscardOps, RequestCounter}
import com.google.common.annotations.VisibleForTesting
import scala.concurrent.{ExecutionContext, Future, blocking}
import scala.util.{Failure, Success}
import scala.concurrent.{ExecutionContext, Future, Promise, blocking}
/** Synchronizes the request processing of phases 3 and 7.
* At the end of phase 3, every request must signal that it has reached
@ -42,10 +34,7 @@ class Phase37Synchronizer(
initRc: RequestCounter,
override val loggerFactory: NamedLoggerFactory,
futureSupervisor: FutureSupervisor,
override val timeouts: ProcessingTimeout,
) extends NamedLogging
with FlagCloseable
with HasCloseContext {
) extends NamedLogging {
/** Maps request timestamps to a promise and a future, which is used to chain each request's evaluation (i.e. filter).
* The future completes with either the pending request data, if it's the first valid call,
@ -76,10 +65,10 @@ class Phase37Synchronizer(
val ts = CantonTimestampWithRequestType[requestType.type](requestId.unwrap, requestType)
implicit val evRequest = ts.pendingRequestRelation
val promise: PromiseUnlessShutdown[Option[
val promise: Promise[Option[
PendingRequestDataOrReplayData[requestType.PendingRequestData]
]] =
mkPromise[Option[
new SupervisedPromise[Option[
PendingRequestDataOrReplayData[requestType.PendingRequestData]
]]("phase37sync-register-request-data", futureSupervisor)
@ -88,7 +77,7 @@ class Phase37Synchronizer(
blocking(synchronized {
val requestRelation: RequestRelation[requestType.PendingRequestData] = RequestRelation(
promise.future
.map(_.onShutdown(None).orElse {
.map(_.orElse {
blocking(synchronized {
pendingRequests.remove_(ts)
})
@ -126,7 +115,7 @@ class Phase37Synchronizer(
)(implicit
traceContext: TraceContext,
ec: ExecutionContext,
): FutureUnlessShutdown[RequestOutcome[requestType.PendingRequestData]] = {
): Future[RequestOutcome[requestType.PendingRequestData]] = {
val ts = CantonTimestampWithRequestType[requestType.type](requestId.unwrap, requestType)
implicit val evRequest = ts.pendingRequestRelation
@ -136,52 +125,46 @@ class Phase37Synchronizer(
logger.debug(
s"Request ${requestId.unwrap}: Request data is waiting to be validated"
)
val promise: PromiseUnlessShutdown[RequestOutcome[requestType.PendingRequestData]] =
mkPromise[RequestOutcome[requestType.PendingRequestData]](
val promise: Promise[RequestOutcome[requestType.PendingRequestData]] =
new SupervisedPromise[RequestOutcome[requestType.PendingRequestData]](
"phase37sync-pending-request-data",
futureSupervisor,
)
val newFut = fut.transformWith {
val newFut = fut.flatMap {
/* either:
(1) another call to awaitConfirmed has already received and successfully validated the data
(2) the request was marked as a timeout
*/
case Success(None) =>
promise.outcome(RequestOutcome.AlreadyServedOrTimeout)
case None =>
promise.success(RequestOutcome.AlreadyServedOrTimeout)
Future.successful(None)
case Success(Some(pData)) =>
filter(pData).transform {
case Success(true) =>
case Some(pData) =>
filter(pData).map {
case true =>
// we need a synchronized block here to avoid conflicts with the outer replace in awaitConfirmed
blocking(synchronized {
// the entry is removed when the first awaitConfirmed with a satisfied predicate is there
pendingRequests.remove_(ts)
})
promise.outcome(RequestOutcome.Success(pData))
Success(None)
case Success(false) =>
promise.outcome(RequestOutcome.Invalid)
Success(Some(pData))
case Failure(exception) =>
promise.tryFailure(exception).discard[Boolean]
Failure(exception)
promise.success(RequestOutcome.Success(pData))
None
case false =>
promise.success(RequestOutcome.Invalid)
Some(pData)
}
case Failure(exception) =>
promise.tryFailure(exception).discard[Boolean]
Future.failed(exception)
}
pendingRequests.replace_[ts.type, RequestRelation[requestType.PendingRequestData]](
ts,
rr.copy(pendingRequestDataFuture = newFut),
)
promise.futureUS
promise.future
case None =>
logger.debug(
s"Request ${requestId.unwrap}: Request data was already returned to another caller" +
s" or has timed out"
)
FutureUnlessShutdown.pure(RequestOutcome.AlreadyServedOrTimeout)
Future.successful(RequestOutcome.AlreadyServedOrTimeout)
}
})
}
@ -229,16 +212,10 @@ object Phase37Synchronizer {
)
final class PendingRequestDataHandle[T <: PendingRequestData](
private val handle: PromiseUnlessShutdown[Option[PendingRequestDataOrReplayData[T]]]
private val handle: Promise[Option[PendingRequestDataOrReplayData[T]]]
) {
def complete(pendingData: Option[PendingRequestDataOrReplayData[T]]): Unit = {
handle.outcome(pendingData)
}
def failed(exception: Throwable): Unit = {
handle.failure(exception)
}
def shutdown(): Unit = {
handle.shutdown()
handle.success(pendingData)
}
}

View File

@ -18,7 +18,11 @@ import com.digitalasset.canton.crypto.{
}
import com.digitalasset.canton.data.{CantonTimestamp, ViewPosition, ViewTree, ViewType}
import com.digitalasset.canton.ledger.api.DeduplicationPeriod
import com.digitalasset.canton.lifecycle.{FutureUnlessShutdown, UnlessShutdown}
import com.digitalasset.canton.lifecycle.{
FutureUnlessShutdown,
PromiseUnlessShutdown,
UnlessShutdown,
}
import com.digitalasset.canton.logging.NamedLoggerFactory
import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting}
import com.digitalasset.canton.participant.protocol.Phase37Synchronizer.RequestOutcome
@ -59,7 +63,6 @@ import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.util.EitherTUtil.{condUnitET, ifThenET}
import com.digitalasset.canton.util.EitherUtil.RichEither
import com.digitalasset.canton.util.FutureInstances.*
import com.digitalasset.canton.util.Thereafter.syntax.ThereafterOps
import com.digitalasset.canton.util.*
import com.digitalasset.canton.version.ProtocolVersion
import com.digitalasset.canton.{DiscardOps, LfPartyId, RequestCounter, SequencerCounter, checked}
@ -447,7 +450,7 @@ abstract class ProtocolProcessor[
)
// use the send callback and a promise to capture the eventual sequenced event read by the submitter
sendResultP = mkPromise[SendResult](
sendResultP = new PromiseUnlessShutdown[SendResult](
"sequenced-event-send-result",
futureSupervisor,
)
@ -613,14 +616,7 @@ abstract class ProtocolProcessor[
.registerRequest(steps.requestType)(RequestId(ts))
)
.map { handleRequestData =>
// If the result is not a success, we still need to complete the request data in some way
performRequestProcessing(ts, rc, sc, handleRequestData, batch, freshOwnTimelyTxF)
.thereafter {
case Failure(exception) => handleRequestData.failed(exception)
case Success(UnlessShutdown.Outcome(Left(_))) => handleRequestData.complete(None)
case Success(UnlessShutdown.AbortedDueToShutdown) => handleRequestData.shutdown()
case _ =>
}
}
}
toHandlerRequest(ts, processedET)
@ -1049,7 +1045,7 @@ abstract class ProtocolProcessor[
timeoutEvent(),
)
)
_ = EitherTUtil.doNotAwaitUS(timeoutET, "Handling timeout failed")
_ = EitherTUtil.doNotAwait(timeoutET, "Handling timeout failed")
signedResponsesTo <- EitherT.right(responsesTo.parTraverse { case (response, recipients) =>
FutureUnlessShutdown.outcomeF(
@ -1425,7 +1421,7 @@ abstract class ProtocolProcessor[
ephemeral.requestTracker.tick(sc, resultTs)
Left(steps.embedResultError(InvalidPendingRequest(requestId)))
}
).flatMap { pendingRequestDataOrReplayData =>
).mapK(FutureUnlessShutdown.outcomeK).flatMap { pendingRequestDataOrReplayData =>
performResultProcessing3(
signedResultBatchE,
unsignedResultE,
@ -1713,7 +1709,7 @@ abstract class ProtocolProcessor[
result: TimeoutResult
)(implicit
traceContext: TraceContext
): EitherT[FutureUnlessShutdown, steps.ResultError, Unit] =
): EitherT[Future, steps.ResultError, Unit] =
if (result.timedOut) {
logger.info(
show"${steps.requestKind.unquoted} request at $requestId timed out without a transaction result message."
@ -1754,15 +1750,13 @@ abstract class ProtocolProcessor[
// No need to clean up the pending submissions because this is handled (concurrently) by schedulePendingSubmissionRemoval
cleanReplay = isCleanReplay(requestCounter, pendingRequestDataOrReplayData)
_ <- EitherT
.right[steps.ResultError](
ephemeral.storedContractManager.deleteIfPending(requestCounter, pendingContracts)
)
.mapK(FutureUnlessShutdown.outcomeK)
_ <- EitherT.right[steps.ResultError](
ephemeral.storedContractManager.deleteIfPending(requestCounter, pendingContracts)
)
_ <- ifThenET(!cleanReplay)(publishEvent()).mapK(FutureUnlessShutdown.outcomeK)
_ <- ifThenET(!cleanReplay)(publishEvent())
} yield ()
} else EitherT.pure[FutureUnlessShutdown, steps.ResultError](())
} else EitherT.pure[Future, steps.ResultError](())
private[this] def isCleanReplay(
requestCounter: RequestCounter,

View File

@ -6,18 +6,14 @@ package com.digitalasset.canton.participant.protocol.conflictdetection
import cats.data.{EitherT, NonEmptyChain}
import cats.syntax.either.*
import com.daml.nameof.NameOf.functionFullName
import com.digitalasset.canton.concurrent.FutureSupervisor
import com.digitalasset.canton.concurrent.{FutureSupervisor, SupervisedPromise}
import com.digitalasset.canton.config.ProcessingTimeout
import com.digitalasset.canton.data.{CantonTimestamp, TaskScheduler, TaskSchedulerMetrics}
import com.digitalasset.canton.lifecycle.UnlessShutdown.AbortedDueToShutdown
import com.digitalasset.canton.lifecycle.{
AsyncOrSyncCloseable,
FlagCloseableAsync,
FutureUnlessShutdown,
HasCloseContext,
PromiseUnlessShutdown,
PromiseUnlessShutdownFactory,
RunOnShutdown,
SyncCloseable,
UnlessShutdown,
}
@ -28,7 +24,7 @@ import com.digitalasset.canton.participant.util.TimeOfChange
import com.digitalasset.canton.protocol.LfContractId
import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.util.{ErrorUtil, FutureUtil, SingleUseCell}
import com.digitalasset.canton.{DiscardOps, RequestCounter, SequencerCounter}
import com.digitalasset.canton.{RequestCounter, SequencerCounter}
import com.google.common.annotations.VisibleForTesting
import scala.annotation.nowarn
@ -57,8 +53,7 @@ private[participant] class NaiveRequestTracker(
)(implicit executionContext: ExecutionContext)
extends RequestTracker
with NamedLogging
with FlagCloseableAsync
with HasCloseContext { self =>
with FlagCloseableAsync {
import NaiveRequestTracker.*
import RequestTracker.*
@ -73,16 +68,6 @@ private[participant] class NaiveRequestTracker(
futureSupervisor,
)
// The task scheduler can decide to close itself if a task fails to execute
// If that happens, close the tracker as well since we won't be able to make progress without a scheduler
taskScheduler.runOnShutdown_(
new RunOnShutdown {
override def name: String = "close-request-tracker-due-to-scheduler-shutdown"
override def done: Boolean = isClosing
override def run(): Unit = self.close()
}
)(TraceContext.empty)
/** Maps request counters to the data associated with a request.
*
* A request resides in the map from the call to [[RequestTracker!.addRequest]] until some time after
@ -126,14 +111,7 @@ private[participant] class NaiveRequestTracker(
),
)
val data = RequestData.mk(
sc,
requestTimestamp,
decisionTime,
activenessSet,
this,
futureSupervisor,
)
val data = RequestData.mk(sc, requestTimestamp, decisionTime, activenessSet, futureSupervisor)
requests.putIfAbsent(rc, data) match {
case None =>
@ -156,7 +134,7 @@ private[participant] class NaiveRequestTracker(
val f = conflictDetector.registerActivenessSet(rc, activenessSet).map { _ =>
// Tick the task scheduler only after all states have been prefetched into the conflict detector
taskScheduler.addTick(sc, requestTimestamp)
RequestFutures(data.activenessResult.futureUS, data.timeoutResult.futureUS)
RequestFutures(data.activenessResult.futureUS, data.timeoutResult.future)
}
Right(f)
@ -165,7 +143,7 @@ private[participant] class NaiveRequestTracker(
logger.debug(withRC(rc, s"Added a second time to the request tracker"))
Right(
FutureUnlessShutdown.pure(
RequestFutures(oldData.activenessResult.futureUS, oldData.timeoutResult.futureUS)
RequestFutures(oldData.activenessResult.futureUS, oldData.timeoutResult.future)
)
)
} else {
@ -209,7 +187,7 @@ private[participant] class NaiveRequestTracker(
rc,
sc,
requestData.requestTimestamp,
requestData.commitSetPromise.futureUS,
requestData.commitSetPromise.future,
commitTime,
)
val data = FinalizationData(resultTimestamp, commitTime)(task.finalizationResult)
@ -218,7 +196,7 @@ private[participant] class NaiveRequestTracker(
logger.debug(
withRC(rc, s"New result at $resultTimestamp signalled to the request tracker")
)
requestData.timeoutResult outcome NoTimeout
requestData.timeoutResult success NoTimeout
taskScheduler.scheduleTask(task)
taskScheduler.addTick(sc, resultTimestamp)
Right(())
@ -244,7 +222,7 @@ private[participant] class NaiveRequestTracker(
], Unit]] = {
def tryAddCommitSet(
commitSetPromise: PromiseUnlessShutdown[CommitSet],
commitSetPromise: Promise[CommitSet],
finalizationResult: PromiseUnlessShutdown[
Either[NonEmptyChain[RequestTrackerStoreError], Unit]
],
@ -252,9 +230,7 @@ private[participant] class NaiveRequestTracker(
RequestTrackerStoreError
], Unit]] = {
// Complete the promise only if we're not shutting down.
performUnlessClosing(functionFullName) {
commitSetPromise.tryComplete(commitSet.map(UnlessShutdown.Outcome(_)))
} match {
performUnlessClosing(functionFullName) { commitSetPromise.tryComplete(commitSet) } match {
case UnlessShutdown.AbortedDueToShutdown =>
// Try to clean up as good as possible even though recovery of the ephemeral state will ultimately
// take care of the cleaning up.
@ -270,17 +246,9 @@ private[participant] class NaiveRequestTracker(
withRC(rc, s"Completed commit set promise does not contain a value")
)
)
if (oldCommitSet == commitSet.map(UnlessShutdown.Outcome(_))) {
if (oldCommitSet == commitSet) {
logger.debug(withRC(rc, s"Commit set added a second time."))
Right(EitherT(finalizationResult.futureUS))
} else if (oldCommitSet.toEither.contains(AbortedDueToShutdown)) {
logger.debug(
withRC(
rc,
s"Old commit set was aborted due to shutdown. New commit set will be ignored.",
)
)
Left(CommitSetAlreadyExists(rc))
} else {
logger.warn(withRC(rc, s"Commit set with different parameters added a second time."))
Left(CommitSetAlreadyExists(rc))
@ -368,7 +336,7 @@ private[participant] class NaiveRequestTracker(
result.map { actRes =>
logger.trace(withRC(rc, s"Activeness result $actRes"))
}
}.tapOnShutdown(activenessResult.shutdown())
}
override def pretty: Pretty[this.type] = prettyOfClass(
param("timestamp", _.timestamp),
@ -388,7 +356,7 @@ private[participant] class NaiveRequestTracker(
*/
private[this] class TriggerTimeout(
val rc: RequestCounter,
timeoutPromise: PromiseUnlessShutdown[TimeoutResult],
timeoutPromise: Promise[TimeoutResult],
val requestTimestamp: CantonTimestamp,
override val timestamp: CantonTimestamp,
override val sequencerCounter: SequencerCounter,
@ -417,7 +385,7 @@ private[participant] class NaiveRequestTracker(
* the promise because this would complete the timeout promise too early, as the conflict detector has
* not yet released the locks held by the request.
*/
timeoutPromise outcome Timeout
timeoutPromise success Timeout
()
}
} else { FutureUnlessShutdown.unit }
@ -430,7 +398,7 @@ private[participant] class NaiveRequestTracker(
param("rc", _.rc),
)
override def close(): Unit = timeoutPromise.shutdown()
override def close(): Unit = ()
}
/** The action for finalizing a request by committing and rolling back contract changes.
@ -444,7 +412,7 @@ private[participant] class NaiveRequestTracker(
rc: RequestCounter,
override val sequencerCounter: SequencerCounter,
requestTimestamp: CantonTimestamp,
commitSetFuture: FutureUnlessShutdown[CommitSet],
commitSetFuture: Future[CommitSet],
commitTime: CantonTimestamp,
)(override implicit val traceContext: TraceContext)
extends TimedTask(commitTime, sequencerCounter, Kind.Finalization) {
@ -454,7 +422,7 @@ private[participant] class NaiveRequestTracker(
*/
val finalizationResult: PromiseUnlessShutdown[
Either[NonEmptyChain[RequestTrackerStoreError], Unit]
] = mkPromise[Either[NonEmptyChain[RequestTrackerStoreError], Unit]](
] = new PromiseUnlessShutdown[Either[NonEmptyChain[RequestTrackerStoreError], Unit]](
"finalization-result",
futureSupervisor,
)
@ -466,28 +434,20 @@ private[participant] class NaiveRequestTracker(
*/
override def perform(): FutureUnlessShutdown[Unit] =
performUnlessClosingUSF("finalize-request") {
commitSetFuture.transformWith {
FutureUnlessShutdown.outcomeF(commitSetFuture).transformWith {
case Success(UnlessShutdown.Outcome(commitSet)) =>
logger.debug(withRC(rc, s"Finalizing at $commitTime"))
conflictDetector
.finalizeRequest(commitSet, TimeOfChange(rc, requestTimestamp))
.transform {
case Success(UnlessShutdown.Outcome(storeFuture)) =>
// The finalization is complete when the conflict detection stores have been updated
finalizationResult.completeWith(storeFuture)
// Immediately evict the request
Success(UnlessShutdown.Outcome(evictRequest(rc)))
case Success(UnlessShutdown.AbortedDueToShutdown) =>
finalizationResult.shutdown()
Success(UnlessShutdown.AbortedDueToShutdown)
case Failure(e) =>
finalizationResult.tryFailure(e).discard[Boolean]
Failure(e)
.map { storeFuture =>
// The finalization is complete when the conflict detection stores have been updated
finalizationResult.completeWith(storeFuture.unwrap)
// Immediately evict the request
evictRequest(rc)
}
case Success(UnlessShutdown.AbortedDueToShutdown) =>
logger.debug(withRC(rc, s"Aborted finalizing at $commitTime due to shutdown"))
finalizationResult.shutdown()
FutureUnlessShutdown.abortedDueToShutdown
case Failure(ex) =>
@ -602,9 +562,9 @@ private[conflictdetection] object NaiveRequestTracker {
activenessSet: ActivenessSet,
)(
val activenessResult: PromiseUnlessShutdown[ActivenessResult],
val timeoutResult: PromiseUnlessShutdown[TimeoutResult],
val timeoutResult: Promise[TimeoutResult],
val finalizationDataCell: SingleUseCell[FinalizationData],
val commitSetPromise: PromiseUnlessShutdown[CommitSet],
val commitSetPromise: Promise[CommitSet],
)
private[NaiveRequestTracker] object RequestData {
@ -613,19 +573,22 @@ private[conflictdetection] object NaiveRequestTracker {
requestTimestamp: CantonTimestamp,
decisionTime: CantonTimestamp,
activenessSet: ActivenessSet,
promiseUSFactory: PromiseUnlessShutdownFactory,
futureSupervisor: FutureSupervisor,
)(implicit elc: ErrorLoggingContext, executionContext: ExecutionContext): RequestData =
)(implicit
elc: ErrorLoggingContext,
ec: ExecutionContext,
): RequestData =
new RequestData(
sequencerCounter = sc,
requestTimestamp = requestTimestamp,
decisionTime = decisionTime,
activenessSet = activenessSet,
)(
activenessResult = promiseUSFactory.mkPromise("activeness-result", futureSupervisor),
timeoutResult = promiseUSFactory.mkPromise("timeout-result", futureSupervisor),
activenessResult =
new PromiseUnlessShutdown[ActivenessResult]("activeness-result", futureSupervisor),
timeoutResult = Promise[TimeoutResult](),
finalizationDataCell = new SingleUseCell[FinalizationData],
commitSetPromise = promiseUSFactory.mkPromise("commit-set", futureSupervisor),
commitSetPromise = new SupervisedPromise[CommitSet]("commit-set", futureSupervisor),
)
}

View File

@ -308,7 +308,7 @@ object RequestTracker {
*/
final case class RequestFutures(
activenessResult: FutureUnlessShutdown[ActivenessResult],
timeoutResult: FutureUnlessShutdown[TimeoutResult],
timeoutResult: Future[TimeoutResult],
)
/** Indicates whether the request has timed out. */

View File

@ -47,6 +47,7 @@ final case class TransferData(
def transferId: TransferId = TransferId(transferOutRequest.sourceDomain, transferOutTimestamp)
def sourceMediator: MediatorRef = transferOutRequest.mediator
def transferCounter: TransferCounterO = transferOutRequest.transferCounter
def addTransferOutResult(result: DeliveredTransferOutResult): Option[TransferData] =

View File

@ -10,13 +10,7 @@ import cats.syntax.traverse.*
import com.daml.nonempty.{NonEmpty, NonEmptyUtil}
import com.digitalasset.canton.crypto.{DomainSnapshotSyncCryptoApi, HashOps, Signature}
import com.digitalasset.canton.data.ViewType.TransferOutViewType
import com.digitalasset.canton.data.{
CantonTimestamp,
FullTransferOutTree,
TransferSubmitterMetadata,
ViewPosition,
ViewType,
}
import com.digitalasset.canton.data.*
import com.digitalasset.canton.ledger.participant.state.v2.CompletionInfo
import com.digitalasset.canton.lifecycle.FutureUnlessShutdown
import com.digitalasset.canton.logging.{NamedLoggerFactory, NamedLogging}
@ -161,11 +155,15 @@ class TransferOutProcessingSteps(
.leftMap(_ => TransferOutProcessorError.TransferCounterOverflow)
)
creatingTransactionId <- EitherT.fromEither[FutureUnlessShutdown](
storedContract.creatingTransactionIdO.toRight(CreatingTransactionIdNotFound(contractId))
)
validated <- TransferOutRequest.validated(
participantId,
timeProof,
contractId,
templateId,
creatingTransactionId,
storedContract.contract,
submitterMetadata,
stakeholders,
domainId,
@ -421,7 +419,28 @@ class TransferOutProcessingSteps(
// Since the transfer out request should be sent only to participants that host a stakeholder of the contract,
// we can expect to find the contract in the contract store.
storedContract <- getStoredContract(contractLookup, fullTree.contractId)
contractWithTransactionId <-
fullTree.tree.view.tryUnwrap match {
case view: TransferOutViewCNTestNet =>
// TODO(i15090): Validate contract data against contract id and contract metadata against contract data
EitherT.rightT[FutureUnlessShutdown, TransferProcessorError](
WithTransactionId(view.contract, view.creatingTransactionId)
)
case _: TransferOutViewV0 | _: TransferOutViewV4 =>
for {
storedContract <- getStoredContract(contractLookup, fullTree.contractId)
// Since the participant hosts a stakeholder, it should find the creating transaction ID in the contract store
creatingTransactionId <- EitherT.fromEither[FutureUnlessShutdown](
storedContract.creatingTransactionIdO
.toRight[TransferProcessorError](
CreatingTransactionIdNotFound(storedContract.contractId)
)
)
} yield WithTransactionId(storedContract.contract, creatingTransactionId)
}
WithTransactionId(contract, creatingTransactionId) = contractWithTransactionId
transferringParticipant = fullTree.adminParties.contains(participantId.adminParty.toLf)
@ -433,8 +452,8 @@ class TransferOutProcessingSteps(
_ <- TransferOutValidation(
fullTree,
storedContract.contract.metadata.stakeholders,
storedContract.contract.rawContractInstance.contractInstance.unversioned.template,
contract.metadata.stakeholders,
contract.rawContractInstance.contractInstance.unversioned.template,
sourceDomainProtocolVersion,
sourceSnapshot.ipsSnapshot,
targetTopology,
@ -448,12 +467,6 @@ class TransferOutProcessingSteps(
fullTree.targetDomain,
)
// Since the participant hosts a stakeholder, it should find the creating transaction ID in the contract store
creatingTransactionId <- EitherT.fromEither[FutureUnlessShutdown](
storedContract.creatingTransactionIdO
.toRight(CreatingTransactionIdNotFound(storedContract.contractId))
)
activenessResult <- EitherT.right(activenessF)
hostedStks <- EitherT.liftF(
@ -468,9 +481,9 @@ class TransferOutProcessingSteps(
rc,
sc,
fullTree.tree.rootHash,
WithContractHash.fromContract(storedContract.contract, fullTree.contractId),
WithContractHash.fromContract(contract, fullTree.contractId),
fullTree.transferCounter,
storedContract.contract.rawContractInstance.contractInstance.unversioned.template,
contract.rawContractInstance.contractInstance.unversioned.template,
transferringParticipant,
fullTree.submitterMetadata,
transferId,
@ -493,7 +506,7 @@ class TransferOutProcessingSteps(
transferOutRequestCounter = rc,
transferOutRequest = fullTree,
transferOutDecisionTime = transferOutDecisionTime,
contract = storedContract.contract,
contract = contract,
creatingTransactionId = creatingTransactionId,
transferOutResult = None,
transferGlobalOffset = None,
@ -502,7 +515,7 @@ class TransferOutProcessingSteps(
transferCoordination.addTransferOutRequest(transferData).mapK(FutureUnlessShutdown.outcomeK)
}
confirmingStakeholders <- EitherT.right(
storedContract.contract.metadata.stakeholders.toList.parTraverseFilter(stakeholder =>
contract.metadata.stakeholders.toList.parTraverseFilter(stakeholder =>
FutureUnlessShutdown.outcomeF(
sourceSnapshot.ipsSnapshot
.canConfirm(participantId, stakeholder)
@ -514,7 +527,7 @@ class TransferOutProcessingSteps(
requestId,
transferringParticipant,
activenessResult,
storedContract.contractId,
contract.contractId,
fullTree.transferCounter,
confirmingStakeholders.toSet,
fullTree.viewHash,

View File

@ -12,10 +12,9 @@ import com.digitalasset.canton.logging.TracedLogger
import com.digitalasset.canton.participant.protocol.CanSubmitTransfer
import com.digitalasset.canton.participant.protocol.transfer.TransferProcessingSteps.{
InvalidTransferCommonData,
InvalidTransferView,
TransferProcessorError,
}
import com.digitalasset.canton.protocol.{LfContractId, LfTemplateId, SourceDomainId, TargetDomainId}
import com.digitalasset.canton.protocol.*
import com.digitalasset.canton.time.TimeProof
import com.digitalasset.canton.topology.client.TopologySnapshot
import com.digitalasset.canton.topology.{MediatorRef, ParticipantId}
@ -37,8 +36,8 @@ final case class TransferOutRequest(
submitterMetadata: TransferSubmitterMetadata,
stakeholders: Set[LfPartyId],
adminParties: Set[LfPartyId],
contractId: LfContractId,
templateId: LfTemplateId,
creatingTransactionId: TransactionId,
contract: SerializableContract,
sourceDomain: SourceDomainId,
sourceProtocolVersion: SourceProtocolVersion,
sourceMediator: MediatorRef,
@ -68,19 +67,18 @@ final case class TransferOutRequest(
sourceProtocolVersion,
)
.leftMap(reason => InvalidTransferCommonData(reason))
view <- TransferOutView
view = TransferOutView
.create(hashOps)(
viewSalt,
submitterMetadata,
contractId,
TransferOutView.templateIdDefaultValue.orValue(templateId, sourceProtocolVersion.v),
creatingTransactionId,
contract,
targetDomain,
targetTimeProof,
sourceProtocolVersion,
targetProtocolVersion,
transferCounter,
)
.leftMap(reason => InvalidTransferView(reason))
tree = TransferOutViewTree(commonData, view, sourceProtocolVersion.v, hashOps)
} yield FullTransferOutTree(tree)
}
@ -91,8 +89,8 @@ object TransferOutRequest {
def validated(
participantId: ParticipantId,
timeProof: TimeProof,
contractId: LfContractId,
templateId: LfTemplateId,
creatingTransactionId: TransactionId,
contract: SerializableContract,
submitterMetadata: TransferSubmitterMetadata,
stakeholders: Set[LfPartyId],
sourceDomain: SourceDomainId,
@ -111,7 +109,10 @@ object TransferOutRequest {
FutureUnlessShutdown,
TransferProcessorError,
TransferOutRequestValidated,
] =
] = {
val contractId = contract.contractId
val templateId = contract.contractInstance.unversioned.template
for {
_ <- CanSubmitTransfer.transferOut(
contractId,
@ -139,8 +140,8 @@ object TransferOutRequest {
submitterMetadata,
stakeholders,
adminPartiesAndRecipients.adminParties,
contractId,
templateId,
creatingTransactionId,
contract,
sourceDomain,
sourceProtocolVersion,
sourceMediator,
@ -152,5 +153,6 @@ object TransferOutRequest {
TransferOutRequestValidated(transferOutRequest, adminPartiesAndRecipients.participants)
}
}
}

View File

@ -147,7 +147,6 @@ class SyncDomainEphemeralState(
startingPoints.cleanReplay.nextRequestCounter,
loggerFactory,
futureSupervisor,
timeouts,
)
val observedTimestampTracker = new WatermarkTracker[CantonTimestamp](
@ -188,7 +187,6 @@ class SyncDomainEphemeralState(
requestTracker,
recordOrderPublisher,
submissionTracker,
phase37Synchronizer,
AsyncCloseable(
"request-journal-flush",
requestJournal.flush(),

View File

@ -22,7 +22,7 @@ import scala.concurrent.Future
class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutionContext {
private def mk(initRc: RequestCounter = RequestCounter(0)): Phase37Synchronizer =
new Phase37Synchronizer(initRc, loggerFactory, FutureSupervisor.Noop, timeouts)
new Phase37Synchronizer(initRc, loggerFactory, FutureSupervisor.Noop)
private val requestId1 = RequestId(CantonTimestamp.ofEpochSecond(1))
private val requestId2 = RequestId(CantonTimestamp.ofEpochSecond(2))
@ -52,7 +52,6 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
)
p37s
.awaitConfirmed(requestType)(requestId1)
.failOnShutdown
.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
}
@ -62,7 +61,6 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
p37s.registerRequest(requestType)(requestId1).complete(None)
p37s
.awaitConfirmed(requestType)(requestId1)
.failOnShutdown
.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
}
@ -77,7 +75,7 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
handle.complete(
Some(pendingRequestData)
)
f.failOnShutdown.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
f.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
}
"return only after reaching confirmed (for request timeout)" in {
@ -88,7 +86,7 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
assert(!f.isCompleted)
handle.complete(None)
f.failOnShutdown.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
f.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
}
"return after request is marked as timeout and the memory cleaned" in {
@ -102,7 +100,6 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
}
p37s
.awaitConfirmed(requestType)(requestId1)
.failOnShutdown
.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
}
@ -119,8 +116,8 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
val f2 = p37s.awaitConfirmed(requestType)(requestId1)
f1.failOnShutdown.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
f2.failOnShutdown.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
f1.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
f2.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
}
"complain if multiple registers have been called for the same requestID" in {
@ -155,10 +152,8 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
val f3 = p37s.awaitConfirmed(requestType)(requestId1)
f1.failOnShutdown.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
forAll(Seq(f2, f3))(fut =>
fut.failOnShutdown.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
)
f1.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
forAll(Seq(f2, f3))(fut => fut.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout)
}
"no valid confirms" in {
@ -189,7 +184,7 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
handle.complete(Some(pendingRequestData))
forAll(Seq(f1, f2, f3))(fut => fut.failOnShutdown.futureValue shouldBe RequestOutcome.Invalid)
forAll(Seq(f1, f2, f3))(fut => fut.futureValue shouldBe RequestOutcome.Invalid)
}
"deal with several calls for the same unconfirmed request with different filters" in {
@ -226,10 +221,8 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
_ => Future.successful(true),
)
f1.failOnShutdown.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
forAll(Seq(f2, f3, f4))(fut =>
fut.failOnShutdown.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
)
f1.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
forAll(Seq(f2, f3, f4))(fut => fut.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout)
}
"deal with several calls for the same confirmed request with different filters" in {
@ -245,13 +238,10 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
val f1 = p37s
.awaitConfirmed(requestType)(requestId1, _ => Future.successful(true))
.failOnShutdown
val f2 = p37s
.awaitConfirmed(requestType)(requestId1, _ => Future.successful(false))
.failOnShutdown
val f3 = p37s
.awaitConfirmed(requestType)(requestId1, _ => Future.successful(true))
.failOnShutdown
f1.futureValue shouldBe RequestOutcome.Success(pendingRequestData0)
forAll(Seq(f2, f3))(fut => fut.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout)
@ -263,13 +253,10 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
)
val f4 = p37s
.awaitConfirmed(requestType)(requestId2, _ => Future.successful(false))
.failOnShutdown
val f5 = p37s
.awaitConfirmed(requestType)(requestId2, _ => Future.successful(true))
.failOnShutdown
val f6 = p37s
.awaitConfirmed(requestType)(requestId2, _ => Future.successful(false))
.failOnShutdown
f4.futureValue shouldBe RequestOutcome.Invalid
f5.futureValue shouldBe RequestOutcome.Success(pendingRequestData1)
@ -287,7 +274,6 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
)
p37s
.awaitConfirmed(requestType)(requestId1)
.failOnShutdown
.futureValue shouldBe RequestOutcome.Success(pendingRequestData)
p37s
@ -334,21 +320,17 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
requestId1,
_ => Future.successful(true),
)
.failOnShutdown
true
},
)
.failOnShutdown
false
})
},
)
.failOnShutdown
false
})
},
)
.failOnShutdown
eventually() {
f1.futureValue shouldBe RequestOutcome.Invalid
@ -371,12 +353,10 @@ class Phase37SynchronizerTest extends AnyWordSpec with BaseTest with HasExecutio
.complete(Some(pendingRequestData))
p37s
.awaitConfirmed(AnotherTestPendingRequestDataType)(requestId1)
.failOnShutdown
.futureValue shouldBe RequestOutcome.AlreadyServedOrTimeout
p37s.awaitConfirmed(requestType)(requestId1).failOnShutdown.futureValue shouldBe RequestOutcome
.Success(
pendingRequestData
)
p37s.awaitConfirmed(requestType)(requestId1).futureValue shouldBe RequestOutcome.Success(
pendingRequestData
)
}
}

View File

@ -684,7 +684,7 @@ private[conflictdetection] trait RequestTrackerTest {
)
_ = enterTick(rt, SequencerCounter(0), CantonTimestamp.Epoch)
_ = enterTick(rt, SequencerCounter(2), ofEpochMilli(10))
timeout <- toF.failOnShutdown("activeness result")
timeout <- toF
_ = assert(timeout.timedOut)
} yield succeed
}
@ -1153,7 +1153,7 @@ private[conflictdetection] trait RequestTrackerTest {
decisionTime,
activenessSet,
).map { case (aR, tR) =>
(aR.failOnShutdown("activeness result"), tR.failOnShutdown("timeout result"))
(aR.failOnShutdown("activeness result"), tR)
}
}
@ -1164,7 +1164,7 @@ private[conflictdetection] trait RequestTrackerTest {
confirmationRequestTimestamp: CantonTimestamp,
decisionTime: CantonTimestamp,
activenessSet: ActivenessSet,
): Future[(FutureUnlessShutdown[ActivenessResult], FutureUnlessShutdown[TimeoutResult])] =
): Future[(FutureUnlessShutdown[ActivenessResult], Future[TimeoutResult])] =
enterCR_US(
rt,
rc,
@ -1183,7 +1183,7 @@ private[conflictdetection] trait RequestTrackerTest {
activenessTimestamp: CantonTimestamp,
decisionTime: CantonTimestamp,
activenessSet: ActivenessSet,
): Future[(FutureUnlessShutdown[ActivenessResult], FutureUnlessShutdown[TimeoutResult])] = {
): Future[(FutureUnlessShutdown[ActivenessResult], Future[TimeoutResult])] = {
val resCR = rt.addRequest(
rc,
sc,

View File

@ -34,11 +34,7 @@ import com.digitalasset.canton.participant.protocol.transfer.TransferProcessingS
StakeholdersMismatch,
SubmittingPartyMustBeStakeholderIn,
}
import com.digitalasset.canton.participant.store.TransferStoreTest.{
coidAbs1,
contract,
transactionId1,
}
import com.digitalasset.canton.participant.store.TransferStoreTest.{contract, transactionId1}
import com.digitalasset.canton.participant.store.memory.*
import com.digitalasset.canton.participant.store.{
MultiDomainEventLog,
@ -212,8 +208,8 @@ class TransferInProcessingStepsTest extends AsyncWordSpec with BaseTest with Has
submitterInfo(party1),
Set(party1, party2), // Party 2 is a stakeholder and therefore a receiving party
Set.empty,
coidAbs1,
TransferStoreTest.templateId,
TransferStoreTest.transactionId1,
TransferStoreTest.contract,
transferId.sourceDomain,
SourceProtocolVersion(testedProtocolVersion),
sourceMediator,

View File

@ -131,8 +131,8 @@ class TransferInValidationTest
submitterInfo(party1),
Set(party1, party2), // Party 2 is a stakeholder and therefore a receiving party
Set.empty,
contractId,
contract.rawContractInstance.contractInstance.unversioned.template,
ExampleTransactionFactory.transactionId(0),
contract,
transferId.sourceDomain,
SourceProtocolVersion(testedProtocolVersion),
MediatorRef(sourceMediator),

View File

@ -8,8 +8,8 @@ import cats.implicits.*
import com.daml.nonempty.{NonEmpty, NonEmptyUtil}
import com.digitalasset.canton.concurrent.FutureSupervisor
import com.digitalasset.canton.config.{CachingConfigs, DefaultProcessingTimeouts}
import com.digitalasset.canton.crypto.DomainSnapshotSyncCryptoApi
import com.digitalasset.canton.crypto.provider.symbolic.SymbolicCrypto
import com.digitalasset.canton.crypto.{DomainSnapshotSyncCryptoApi, HashPurpose}
import com.digitalasset.canton.data.ViewType.TransferOutViewType
import com.digitalasset.canton.data.{
CantonTimestamp,
@ -67,7 +67,6 @@ import com.digitalasset.canton.{
TransferCounter,
TransferCounterO,
}
import com.google.protobuf.ByteString
import org.scalatest.Assertion
import org.scalatest.wordspec.AsyncWordSpec
@ -255,6 +254,19 @@ final class TransferOutProcessingStepsTest
private val timeEvent =
TimeProofTestUtil.mkTimeProof(timestamp = CantonTimestamp.Epoch, targetDomain = targetDomain)
private lazy val contractId = ExampleTransactionFactory.suffixedId(10, 0)
private lazy val contract = ExampleTransactionFactory.asSerializable(
contractId,
contractInstance = ExampleTransactionFactory.contractInstance(templateId = templateId),
metadata = ContractMetadata.tryCreate(
signatories = Set(submitter),
stakeholders = Set(submitter),
maybeKeyWithMaintainers = None,
),
)
private lazy val creatingTransactionId = ExampleTransactionFactory.transactionId(0)
"TransferOutRequest.validated" should {
val testingTopology = createTestingTopologySnapshot(
topology = Map(
@ -265,11 +277,6 @@ final class TransferOutProcessingStepsTest
packages = Seq(templateId.packageId),
)
val contractId = cantonContractIdVersion.fromDiscriminator(
ExampleTransactionFactory.lfHash(10),
Unicum(pureCrypto.digest(HashPurpose.MerkleTreeInnerNode, ByteString.copyFromUtf8("unicum"))),
)
def mkTxOutRes(
stakeholders: Set[LfPartyId],
sourceTopologySnapshot: TopologySnapshot,
@ -279,8 +286,8 @@ final class TransferOutProcessingStepsTest
.validated(
submittingParticipant,
timeEvent,
contractId,
templateId,
creatingTransactionId,
contract,
submitterMetadata(submitter),
stakeholders,
sourceDomain,
@ -460,8 +467,8 @@ final class TransferOutProcessingStepsTest
submitterMetadata = submitterMetadata(submitter),
stakeholders = Set(submitter, party1),
adminParties = Set(adminSubmitter, admin1),
contractId = contractId,
templateId = templateId,
creatingTransactionId = creatingTransactionId,
contract = contract,
sourceDomain = sourceDomain,
sourceProtocolVersion = SourceProtocolVersion(testedProtocolVersion),
sourceMediator = sourceMediator,
@ -502,8 +509,8 @@ final class TransferOutProcessingStepsTest
submitterMetadata = submitterMetadata(submitter),
stakeholders = stakeholders,
adminParties = Set(adminSubmitter, admin3, admin4),
contractId = contractId,
templateId = templateId,
creatingTransactionId = creatingTransactionId,
contract = contract,
sourceDomain = sourceDomain,
sourceProtocolVersion = SourceProtocolVersion(testedProtocolVersion),
sourceMediator = sourceMediator,
@ -524,8 +531,8 @@ final class TransferOutProcessingStepsTest
submitterMetadata = submitterMetadata(submitter),
stakeholders = stakeholders,
adminParties = Set(adminSubmitter, admin1),
contractId = contractId,
templateId = templateId,
creatingTransactionId = creatingTransactionId,
contract = contract,
sourceDomain = sourceDomain,
sourceProtocolVersion = SourceProtocolVersion(testedProtocolVersion),
sourceMediator = sourceMediator,
@ -543,7 +550,6 @@ final class TransferOutProcessingStepsTest
"prepare submission" should {
"succeed without errors" in {
val state = mkState
val contractId = ExampleTransactionFactory.suffixedId(10, 0)
val contract = ExampleTransactionFactory.asSerializable(
contractId,
contractInstance = ExampleTransactionFactory.contractInstance(templateId = templateId),
@ -587,7 +593,6 @@ final class TransferOutProcessingStepsTest
"check that the target domain is not equal to the source domain" in {
val state = mkState
val contractId = ExampleTransactionFactory.suffixedId(10, 0)
val contract = ExampleTransactionFactory.asSerializable(
contractId,
contractInstance = ExampleTransactionFactory.contractInstance(),
@ -621,7 +626,6 @@ final class TransferOutProcessingStepsTest
"forbid transfer if the target domain does not support transfer counters and the source domain supports them" in {
val targetProtocolVersion = TargetProtocolVersion(ProtocolVersion.v4)
val state = mkState
val contractId = ExampleTransactionFactory.suffixedId(10, 0)
val contract = ExampleTransactionFactory.asSerializable(
contractId,
contractInstance = ExampleTransactionFactory.contractInstance(templateId = templateId),
@ -678,13 +682,12 @@ final class TransferOutProcessingStepsTest
}
"receive request" should {
val contractId = ExampleTransactionFactory.suffixedId(10, 0)
val outRequest = TransferOutRequest(
submitterMetadata = submitterMetadata(party1),
Set(party1),
Set(party1),
contractId,
templateId = templateId,
creatingTransactionId,
contract,
sourceDomain,
SourceProtocolVersion(testedProtocolVersion),
sourceMediator,
@ -747,7 +750,6 @@ final class TransferOutProcessingStepsTest
transferOutProcessingSteps: TransferOutProcessingSteps
) = {
val state = mkState
val contractId = ExampleTransactionFactory.suffixedId(10, 0)
val metadata = ContractMetadata.tryCreate(Set.empty, Set(party1), None)
val contract = ExampleTransactionFactory.asSerializable(
contractId,
@ -759,8 +761,8 @@ final class TransferOutProcessingStepsTest
submitterMetadata = submitterMetadata(party1),
Set(party1),
Set(submittingParticipant.adminParty.toLf),
contractId,
templateId = templateId,
creatingTransactionId,
contract,
sourceDomain,
SourceProtocolVersion(testedProtocolVersion),
sourceMediator,
@ -835,7 +837,6 @@ final class TransferOutProcessingStepsTest
"get commit set and contracts to be stored and event" should {
"succeed without errors" in {
val state = mkState
val contractId = ExampleTransactionFactory.suffixedId(10, 0)
val contractHash = ExampleTransactionFactory.lfHash(0)
val transferId = TransferId(sourceDomain, CantonTimestamp.Epoch)
val rootHash = mock[RootHash]

View File

@ -24,7 +24,6 @@ import com.digitalasset.canton.version.Transfer.{SourceProtocolVersion, TargetPr
import org.scalatest.wordspec.AsyncWordSpec
import java.util.UUID
import scala.Right
class TransferOutValidationTest
extends AsyncWordSpec
@ -111,7 +110,7 @@ class TransferOutValidationTest
val validation = mkTransferOutValidation(
stakeholders,
sourcePV,
contract.rawContractInstance.contractInstance.unversioned.template,
templateId,
initialTransferCounter,
)
for {
@ -125,7 +124,7 @@ class TransferOutValidationTest
val validation = mkTransferOutValidation(
stakeholders.union(Set(receiverParty2)),
sourcePV,
contract.rawContractInstance.contractInstance.unversioned.template,
templateId,
initialTransferCounter,
)
for {
@ -150,7 +149,7 @@ class TransferOutValidationTest
} yield {
if (sourcePV.v < ProtocolVersion.CNTestNet) {
res shouldBe Right(())
} else res shouldBe Left(TemplateIdMismatch(wrongTemplateId.leftSide, templateId.leftSide))
} else res shouldBe Left(TemplateIdMismatch(templateId.leftSide, wrongTemplateId.leftSide))
}
}
@ -160,7 +159,7 @@ class TransferOutValidationTest
val validation = mkTransferOutValidation(
stakeholders,
newSourcePV,
contract.rawContractInstance.contractInstance.unversioned.template,
templateId,
transferCounter,
).value
@ -184,7 +183,7 @@ class TransferOutValidationTest
def mkTransferOutValidation(
newStakeholders: Set[LfPartyId],
sourceProtocolVersion: SourceProtocolVersion,
newTemplateId: LfTemplateId,
expectedTemplateId: LfTemplateId,
transferCounter: TransferCounterO,
): EitherT[FutureUnlessShutdown, TransferProcessorError, Unit] = {
val transferOutRequest = TransferOutRequest(
@ -192,8 +191,8 @@ class TransferOutValidationTest
// receiverParty2 is not a stakeholder on a contract, but it is listed as stakeholder here
newStakeholders,
Set(participant.adminParty.toLf),
contractId,
newTemplateId,
ExampleTransactionFactory.transactionId(0),
contract,
transferId.sourceDomain,
sourceProtocolVersion,
MediatorRef(sourceMediator),
@ -214,7 +213,7 @@ class TransferOutValidationTest
TransferOutValidation(
fullTransferOutTree,
stakeholders,
templateId,
expectedTemplateId,
sourceProtocolVersion,
identityFactory.topologySnapshot(),
Some(identityFactory.topologySnapshot()),

View File

@ -34,12 +34,14 @@ import com.digitalasset.canton.protocol.ExampleTransactionFactory.{
import com.digitalasset.canton.protocol.messages.*
import com.digitalasset.canton.protocol.{
ContractMetadata,
ExampleTransactionFactory,
LfContractId,
LfTemplateId,
RequestId,
SerializableContract,
SourceDomainId,
TargetDomainId,
TransactionId,
TransferId,
}
import com.digitalasset.canton.sequencing.protocol.*
@ -1192,6 +1194,7 @@ object TransferStoreTest extends EitherValues with NoTracing {
sourceMediator: MediatorRef,
submittingParty: LfPartyId = LfPartyId.assertFromString("submitter"),
targetDomainId: TargetDomainId,
creatingTransactionId: TransactionId = ExampleTransactionFactory.transactionId(0),
contract: SerializableContract = contract,
transferOutGlobalOffset: Option[GlobalOffset] = None,
): Future[TransferData] = {
@ -1209,8 +1212,8 @@ object TransferStoreTest extends EitherValues with NoTracing {
submitterMetadata(submittingParty),
Set(submittingParty),
Set.empty,
contract.contractId,
templateId = templateId,
creatingTransactionId,
contract,
transferId.sourceDomain,
SourceProtocolVersion(BaseTest.testedProtocolVersion),
sourceMediator,
@ -1251,6 +1254,7 @@ object TransferStoreTest extends EitherValues with NoTracing {
transferId: TransferId,
sourceMediator: MediatorId,
submitter: LfPartyId = LfPartyId.assertFromString("submitter"),
creatingTransactionId: TransactionId = transactionId1,
contract: SerializableContract = contract,
transferOutGlobalOffset: Option[GlobalOffset] = None,
) =
@ -1259,6 +1263,7 @@ object TransferStoreTest extends EitherValues with NoTracing {
MediatorRef(sourceMediator),
submitter,
targetDomain,
creatingTransactionId,
contract,
transferOutGlobalOffset,
)