mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-10 10:46:11 +03:00
update canton to be4893cc (#17678)
CHANGELOG_BEGIN CHANGELOG_END Co-authored-by: Azure Pipelines Daml Build <support@digitalasset.com>
This commit is contained in:
parent
b12eb2adb6
commit
16aa217692
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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],
|
||||
)
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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],
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
@ -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 ()
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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] =
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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]
|
||||
|
@ -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()),
|
||||
|
@ -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,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user