update canton to 3.0.0-snapshot.100000000.20240308.12805.0.vd86a0688 (#18696)

* update canton to 3.0.0-snapshot.100000000.20240308.12805.0.vd86a0688

tell-slack: canton

* fix codegen tests

---------

Co-authored-by: Azure Pipelines Daml Build <support@digitalasset.com>
Co-authored-by: Remy Haemmerle <Remy.Haemmerle@daml.com>
This commit is contained in:
azure-pipelines[bot] 2024-03-11 12:17:46 +01:00 committed by GitHub
parent 1e671b6356
commit 405f1ae6f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
83 changed files with 1257 additions and 1106 deletions

View File

@ -43,32 +43,109 @@ import com.daml.ledger.api.v2.admin.user_management_service.{
User,
UserManagementServiceGrpc,
}
import com.daml.ledger.api.v2.checkpoint.Checkpoint
import com.daml.ledger.api.v2.command_completion_service.CommandCompletionServiceGrpc.CommandCompletionServiceStub
import com.daml.ledger.api.v2.command_completion_service.{
CommandCompletionServiceGrpc,
CompletionStreamRequest,
CompletionStreamResponse,
}
import com.daml.ledger.api.v2.command_service.CommandServiceGrpc.CommandServiceStub
import com.daml.ledger.api.v2.command_service.{
CommandServiceGrpc,
SubmitAndWaitForTransactionResponse,
SubmitAndWaitForTransactionTreeResponse,
SubmitAndWaitRequest,
}
import com.daml.ledger.api.v2.command_submission_service.CommandSubmissionServiceGrpc.CommandSubmissionServiceStub
import com.daml.ledger.api.v2.command_submission_service.{
CommandSubmissionServiceGrpc,
SubmitReassignmentRequest,
SubmitReassignmentResponse,
SubmitRequest,
SubmitResponse,
}
import com.daml.ledger.api.v2.commands.{Command, Commands, DisclosedContract}
import com.daml.ledger.api.v2.completion.Completion
import com.daml.ledger.api.v2.event.CreatedEvent
import com.daml.ledger.api.v2.event_query_service.EventQueryServiceGrpc.EventQueryServiceStub
import com.daml.ledger.api.v2.event_query_service.{
EventQueryServiceGrpc,
GetEventsByContractIdRequest,
GetEventsByContractIdResponse,
}
import com.daml.ledger.api.v2.participant_offset.ParticipantOffset
import com.daml.ledger.api.v2.reassignment.{AssignedEvent, Reassignment, UnassignedEvent}
import com.daml.ledger.api.v2.reassignment_command.{
AssignCommand,
ReassignmentCommand,
UnassignCommand,
}
import com.daml.ledger.api.v2.state_service.StateServiceGrpc.StateServiceStub
import com.daml.ledger.api.v2.state_service.{
GetActiveContractsRequest,
GetActiveContractsResponse,
GetConnectedDomainsRequest,
GetConnectedDomainsResponse,
GetLedgerEndRequest,
GetLedgerEndResponse,
StateServiceGrpc,
}
import com.daml.ledger.api.v2.testing.time_service.TimeServiceGrpc.TimeServiceStub
import com.daml.ledger.api.v2.testing.time_service.{GetTimeRequest, SetTimeRequest, TimeServiceGrpc}
import com.digitalasset.canton.LfPartyId
import com.daml.ledger.api.v2.testing.time_service.{
GetTimeRequest,
GetTimeResponse,
SetTimeRequest,
TimeServiceGrpc,
}
import com.daml.ledger.api.v2.transaction.{Transaction, TransactionTree}
import com.daml.ledger.api.v2.transaction_filter.{
Filters,
InclusiveFilters,
TemplateFilter,
TransactionFilter,
}
import com.daml.ledger.api.v2.update_service.UpdateServiceGrpc.UpdateServiceStub
import com.daml.ledger.api.v2.update_service.{
GetTransactionByIdRequest,
GetTransactionTreeResponse,
GetUpdateTreesResponse,
GetUpdatesRequest,
GetUpdatesResponse,
UpdateServiceGrpc,
}
import com.digitalasset.canton.admin.api.client.commands.GrpcAdminCommand.{
DefaultUnboundedTimeout,
ServerEnforcedTimeout,
TimeoutType,
}
import com.digitalasset.canton.admin.api.client.data.{
LedgerApiUser,
LedgerMeteringReport,
ListLedgerApiUsersResult,
TemplateId,
UserRights,
}
import com.digitalasset.canton.config.RequireTypes.PositiveInt
import com.digitalasset.canton.data.CantonTimestamp
import com.digitalasset.canton.ledger.api.domain
import com.digitalasset.canton.ledger.api.domain.{IdentityProviderId, JwksUrl}
import com.digitalasset.canton.ledger.api.{DeduplicationPeriod, domain}
import com.digitalasset.canton.ledger.client.services.admin.IdentityProviderConfigClient
import com.digitalasset.canton.logging.ErrorLoggingContext
import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting}
import com.digitalasset.canton.networking.grpc.ForwardingStreamObserver
import com.digitalasset.canton.protocol.LfContractId
import com.digitalasset.canton.serialization.ProtoConverter
import com.digitalasset.canton.topology.PartyId
import com.digitalasset.canton.topology.{DomainId, PartyId}
import com.digitalasset.canton.util.BinaryFileUtil
import com.digitalasset.canton.{LfPackageId, LfPartyId, config}
import com.google.protobuf.empty.Empty
import com.google.protobuf.field_mask.FieldMask
import io.grpc.*
import io.grpc.stub.StreamObserver
import java.time.Instant
import java.util.UUID
import java.util.concurrent.ScheduledExecutorService
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ExecutionContext, Future}
@ -800,6 +877,736 @@ object LedgerApiCommands {
}
}
trait SubscribeBase[Req, Resp, Res] extends GrpcAdminCommand[Req, AutoCloseable, AutoCloseable] {
// The subscription should never be cut short because of a gRPC timeout
override def timeoutType: TimeoutType = ServerEnforcedTimeout
def observer: StreamObserver[Res]
def doRequest(
service: this.Svc,
request: Req,
rawObserver: StreamObserver[Resp],
): Unit
def extractResults(response: Resp): IterableOnce[Res]
implicit def loggingContext: ErrorLoggingContext
override def submitRequest(
service: this.Svc,
request: Req,
): Future[AutoCloseable] = {
val rawObserver = new ForwardingStreamObserver[Resp, Res](observer, extractResults)
val context = Context.current().withCancellation()
context.run(() => doRequest(service, request, rawObserver))
Future.successful(context)
}
override def handleResponse(response: AutoCloseable): Either[String, AutoCloseable] = Right(
response
)
}
object UpdateService {
sealed trait UpdateTreeWrapper {
def updateId: String
}
sealed trait UpdateWrapper {
def updateId: String
def createEvent: Option[CreatedEvent] =
this match {
case UpdateService.TransactionWrapper(t) => t.events.headOption.map(_.getCreated)
case u: UpdateService.AssignedWrapper => u.assignedEvent.createdEvent
case _: UpdateService.UnassignedWrapper => None
}
}
object UpdateWrapper {
def isUnassigedWrapper(wrapper: UpdateWrapper): Boolean = wrapper match {
case UnassignedWrapper(_, _) => true
case _ => false
}
}
final case class TransactionTreeWrapper(transactionTree: TransactionTree)
extends UpdateTreeWrapper {
override def updateId: String = transactionTree.updateId
}
final case class TransactionWrapper(transaction: Transaction) extends UpdateWrapper {
override def updateId: String = transaction.updateId
}
sealed trait ReassignmentWrapper extends UpdateTreeWrapper with UpdateWrapper {
def reassignment: Reassignment
def unassignId: String = reassignment.getUnassignedEvent.unassignId
def offset: ParticipantOffset = ParticipantOffset(
ParticipantOffset.Value.Absolute(reassignment.offset)
)
}
object ReassignmentWrapper {
def apply(reassignment: Reassignment): ReassignmentWrapper = {
val event = reassignment.event
event.assignedEvent
.map[ReassignmentWrapper](AssignedWrapper(reassignment, _))
.orElse(
event.unassignedEvent.map[ReassignmentWrapper](UnassignedWrapper(reassignment, _))
)
.getOrElse(
throw new IllegalStateException(
s"Invalid reassignment event (only supported UnassignedEvent and AssignedEvent): ${reassignment.event}"
)
)
}
}
final case class AssignedWrapper(reassignment: Reassignment, assignedEvent: AssignedEvent)
extends ReassignmentWrapper {
override def updateId: String = reassignment.updateId
}
final case class UnassignedWrapper(reassignment: Reassignment, unassignedEvent: UnassignedEvent)
extends ReassignmentWrapper {
override def updateId: String = reassignment.updateId
}
trait BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = UpdateServiceStub
override def createService(channel: ManagedChannel): UpdateServiceStub =
UpdateServiceGrpc.stub(channel)
}
trait SubscribeUpdateBase[Resp, Res]
extends BaseCommand[GetUpdatesRequest, AutoCloseable, AutoCloseable]
with SubscribeBase[GetUpdatesRequest, Resp, Res] {
def beginExclusive: ParticipantOffset
def endInclusive: Option[ParticipantOffset]
def filter: TransactionFilter
def verbose: Boolean
override def createRequest(): Either[String, GetUpdatesRequest] = Right {
GetUpdatesRequest(
beginExclusive = Some(beginExclusive),
endInclusive = endInclusive,
verbose = verbose,
filter = Some(filter),
)
}
}
final case class SubscribeTrees(
override val observer: StreamObserver[UpdateTreeWrapper],
override val beginExclusive: ParticipantOffset,
override val endInclusive: Option[ParticipantOffset],
override val filter: TransactionFilter,
override val verbose: Boolean,
)(override implicit val loggingContext: ErrorLoggingContext)
extends SubscribeUpdateBase[GetUpdateTreesResponse, UpdateTreeWrapper] {
override def doRequest(
service: UpdateServiceStub,
request: GetUpdatesRequest,
rawObserver: StreamObserver[GetUpdateTreesResponse],
): Unit =
service.getUpdateTrees(request, rawObserver)
override def extractResults(
response: GetUpdateTreesResponse
): IterableOnce[UpdateTreeWrapper] =
response.update.transactionTree
.map[UpdateTreeWrapper](TransactionTreeWrapper)
.orElse(response.update.reassignment.map(ReassignmentWrapper(_)))
}
final case class SubscribeFlat(
override val observer: StreamObserver[UpdateWrapper],
override val beginExclusive: ParticipantOffset,
override val endInclusive: Option[ParticipantOffset],
override val filter: TransactionFilter,
override val verbose: Boolean,
)(override implicit val loggingContext: ErrorLoggingContext)
extends SubscribeUpdateBase[GetUpdatesResponse, UpdateWrapper] {
override def doRequest(
service: UpdateServiceStub,
request: GetUpdatesRequest,
rawObserver: StreamObserver[GetUpdatesResponse],
): Unit =
service.getUpdates(request, rawObserver)
override def extractResults(response: GetUpdatesResponse): IterableOnce[UpdateWrapper] =
response.update.transaction
.map[UpdateWrapper](TransactionWrapper)
.orElse(response.update.reassignment.map(ReassignmentWrapper(_)))
}
final case class GetTransactionById(parties: Set[LfPartyId], id: String)(implicit
ec: ExecutionContext
) extends BaseCommand[GetTransactionByIdRequest, GetTransactionTreeResponse, Option[
TransactionTree
]]
with PrettyPrinting {
override def createRequest(): Either[String, GetTransactionByIdRequest] = Right {
GetTransactionByIdRequest(
updateId = id,
requestingParties = parties.toSeq,
)
}
override def submitRequest(
service: UpdateServiceStub,
request: GetTransactionByIdRequest,
): Future[GetTransactionTreeResponse] = {
// The Ledger API will throw an error if it can't find a transaction by ID.
// However, as Canton is distributed, a transaction ID might show up later, so we don't treat this as
// an error and change it to a None
service.getTransactionTreeById(request).recover {
case e: StatusRuntimeException if e.getStatus.getCode == Status.Code.NOT_FOUND =>
GetTransactionTreeResponse(None)
}
}
override def handleResponse(
response: GetTransactionTreeResponse
): Either[String, Option[TransactionTree]] =
Right(response.transaction)
override def pretty: Pretty[GetTransactionById] =
prettyOfClass(
param("id", _.id.unquoted),
param("parties", _.parties),
)
}
}
private[commands] trait SubmitCommand extends PrettyPrinting {
def actAs: Seq[LfPartyId]
def readAs: Seq[LfPartyId]
def commands: Seq[Command]
def workflowId: String
def commandId: String
def deduplicationPeriod: Option[DeduplicationPeriod]
def submissionId: String
def minLedgerTimeAbs: Option[Instant]
def disclosedContracts: Seq[DisclosedContract]
def domainId: Option[DomainId]
def applicationId: String
def packageIdSelectionPreference: Seq[LfPackageId]
protected def mkCommand: Commands = Commands(
workflowId = workflowId,
applicationId = applicationId,
commandId = if (commandId.isEmpty) UUID.randomUUID().toString else commandId,
actAs = actAs,
readAs = readAs,
commands = commands,
deduplicationPeriod = deduplicationPeriod.fold(
Commands.DeduplicationPeriod.Empty: Commands.DeduplicationPeriod
) {
case DeduplicationPeriod.DeduplicationDuration(duration) =>
Commands.DeduplicationPeriod.DeduplicationDuration(
ProtoConverter.DurationConverter.toProtoPrimitive(duration)
)
case DeduplicationPeriod.DeduplicationOffset(offset) =>
Commands.DeduplicationPeriod.DeduplicationOffset(
offset.toHexString
)
},
minLedgerTimeAbs = minLedgerTimeAbs.map(ProtoConverter.InstantConverter.toProtoPrimitive),
submissionId = submissionId,
disclosedContracts = disclosedContracts,
domainId = domainId.map(_.toProtoPrimitive).getOrElse(""),
packageIdSelectionPreference = packageIdSelectionPreference.map(_.toString),
)
override def pretty: Pretty[this.type] =
prettyOfClass(
param("actAs", _.actAs),
param("readAs", _.readAs),
param("commandId", _.commandId.singleQuoted),
param("workflowId", _.workflowId.singleQuoted),
param("submissionId", _.submissionId.singleQuoted),
param("deduplicationPeriod", _.deduplicationPeriod),
paramIfDefined("minLedgerTimeAbs", _.minLedgerTimeAbs),
paramWithoutValue("commands"),
)
}
object CommandSubmissionService {
trait BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = CommandSubmissionServiceStub
override def createService(channel: ManagedChannel): CommandSubmissionServiceStub =
CommandSubmissionServiceGrpc.stub(channel)
}
final case class Submit(
override val actAs: Seq[LfPartyId],
override val readAs: Seq[LfPartyId],
override val commands: Seq[Command],
override val workflowId: String,
override val commandId: String,
override val deduplicationPeriod: Option[DeduplicationPeriod],
override val submissionId: String,
override val minLedgerTimeAbs: Option[Instant],
override val disclosedContracts: Seq[DisclosedContract],
override val domainId: Option[DomainId],
override val applicationId: String,
override val packageIdSelectionPreference: Seq[LfPackageId],
) extends SubmitCommand
with BaseCommand[SubmitRequest, SubmitResponse, Unit] {
override def createRequest(): Either[String, SubmitRequest] = Right(
SubmitRequest(commands = Some(mkCommand))
)
override def submitRequest(
service: CommandSubmissionServiceStub,
request: SubmitRequest,
): Future[SubmitResponse] = {
service.submit(request)
}
override def handleResponse(response: SubmitResponse): Either[String, Unit] = Right(())
}
final case class SubmitAssignCommand(
workflowId: String,
applicationId: String,
commandId: String,
submitter: LfPartyId,
submissionId: String,
unassignId: String,
source: DomainId,
target: DomainId,
) extends BaseCommand[SubmitReassignmentRequest, SubmitReassignmentResponse, Unit] {
override def createRequest(): Either[String, SubmitReassignmentRequest] = Right(
SubmitReassignmentRequest(
Some(
ReassignmentCommand(
workflowId = workflowId,
applicationId = applicationId,
commandId = commandId,
submitter = submitter.toString,
command = ReassignmentCommand.Command.AssignCommand(
AssignCommand(
unassignId = unassignId,
source = source.toProtoPrimitive,
target = target.toProtoPrimitive,
)
),
submissionId = submissionId,
)
)
)
)
override def submitRequest(
service: CommandSubmissionServiceStub,
request: SubmitReassignmentRequest,
): Future[SubmitReassignmentResponse] = {
service.submitReassignment(request)
}
override def handleResponse(response: SubmitReassignmentResponse): Either[String, Unit] =
Right(())
}
final case class SubmitUnassignCommand(
workflowId: String,
applicationId: String,
commandId: String,
submitter: LfPartyId,
submissionId: String,
contractId: LfContractId,
source: DomainId,
target: DomainId,
) extends BaseCommand[SubmitReassignmentRequest, SubmitReassignmentResponse, Unit] {
override def createRequest(): Either[String, SubmitReassignmentRequest] = Right(
SubmitReassignmentRequest(
Some(
ReassignmentCommand(
workflowId = workflowId,
applicationId = applicationId,
commandId = commandId,
submitter = submitter.toString,
command = ReassignmentCommand.Command.UnassignCommand(
UnassignCommand(
contractId = contractId.coid.toString,
source = source.toProtoPrimitive,
target = target.toProtoPrimitive,
)
),
submissionId = submissionId,
)
)
)
)
override def submitRequest(
service: CommandSubmissionServiceStub,
request: SubmitReassignmentRequest,
): Future[SubmitReassignmentResponse] = {
service.submitReassignment(request)
}
override def handleResponse(response: SubmitReassignmentResponse): Either[String, Unit] =
Right(())
}
}
object CommandService {
trait BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = CommandServiceStub
override def createService(channel: ManagedChannel): CommandServiceStub =
CommandServiceGrpc.stub(channel)
}
final case class SubmitAndWaitTransactionTree(
override val actAs: Seq[LfPartyId],
override val readAs: Seq[LfPartyId],
override val commands: Seq[Command],
override val workflowId: String,
override val commandId: String,
override val deduplicationPeriod: Option[DeduplicationPeriod],
override val submissionId: String,
override val minLedgerTimeAbs: Option[Instant],
override val disclosedContracts: Seq[DisclosedContract],
override val domainId: Option[DomainId],
override val applicationId: String,
override val packageIdSelectionPreference: Seq[LfPackageId],
) extends SubmitCommand
with BaseCommand[
SubmitAndWaitRequest,
SubmitAndWaitForTransactionTreeResponse,
TransactionTree,
] {
override def createRequest(): Either[String, SubmitAndWaitRequest] =
Right(SubmitAndWaitRequest(commands = Some(mkCommand)))
override def submitRequest(
service: CommandServiceStub,
request: SubmitAndWaitRequest,
): Future[SubmitAndWaitForTransactionTreeResponse] =
service.submitAndWaitForTransactionTree(request)
override def handleResponse(
response: SubmitAndWaitForTransactionTreeResponse
): Either[String, TransactionTree] =
response.transaction.toRight("Received response without any transaction tree")
override def timeoutType: TimeoutType = DefaultUnboundedTimeout
}
final case class SubmitAndWaitTransaction(
override val actAs: Seq[LfPartyId],
override val readAs: Seq[LfPartyId],
override val commands: Seq[Command],
override val workflowId: String,
override val commandId: String,
override val deduplicationPeriod: Option[DeduplicationPeriod],
override val submissionId: String,
override val minLedgerTimeAbs: Option[Instant],
override val disclosedContracts: Seq[DisclosedContract],
override val domainId: Option[DomainId],
override val applicationId: String,
override val packageIdSelectionPreference: Seq[LfPackageId],
) extends SubmitCommand
with BaseCommand[SubmitAndWaitRequest, SubmitAndWaitForTransactionResponse, Transaction] {
override def createRequest(): Either[String, SubmitAndWaitRequest] =
Right(SubmitAndWaitRequest(commands = Some(mkCommand)))
override def submitRequest(
service: CommandServiceStub,
request: SubmitAndWaitRequest,
): Future[SubmitAndWaitForTransactionResponse] =
service.submitAndWaitForTransaction(request)
override def handleResponse(
response: SubmitAndWaitForTransactionResponse
): Either[String, Transaction] =
response.transaction.toRight("Received response without any transaction")
override def timeoutType: TimeoutType = DefaultUnboundedTimeout
}
}
object StateService {
abstract class BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = StateServiceStub
override def createService(channel: ManagedChannel): StateServiceStub =
StateServiceGrpc.stub(channel)
}
final case class LedgerEnd()
extends BaseCommand[GetLedgerEndRequest, GetLedgerEndResponse, ParticipantOffset] {
override def createRequest(): Either[String, GetLedgerEndRequest] =
Right(GetLedgerEndRequest())
override def submitRequest(
service: StateServiceStub,
request: GetLedgerEndRequest,
): Future[GetLedgerEndResponse] =
service.getLedgerEnd(request)
override def handleResponse(
response: GetLedgerEndResponse
): Either[String, ParticipantOffset] =
response.offset.toRight("Empty LedgerEndResponse received without offset")
}
final case class GetConnectedDomains(partyId: LfPartyId)
extends BaseCommand[
GetConnectedDomainsRequest,
GetConnectedDomainsResponse,
GetConnectedDomainsResponse,
] {
override def createRequest(): Either[String, GetConnectedDomainsRequest] =
Right(GetConnectedDomainsRequest(partyId.toString))
override def submitRequest(
service: StateServiceStub,
request: GetConnectedDomainsRequest,
): Future[GetConnectedDomainsResponse] =
service.getConnectedDomains(request)
override def handleResponse(
response: GetConnectedDomainsResponse
): Either[String, GetConnectedDomainsResponse] =
Right(response)
}
final case class GetActiveContracts(
observer: StreamObserver[GetActiveContractsResponse],
parties: Set[LfPartyId],
limit: PositiveInt,
templateFilter: Seq[TemplateId] = Seq.empty,
activeAtOffset: String = "",
verbose: Boolean = true,
timeout: FiniteDuration,
includeCreatedEventBlob: Boolean = false,
)(override implicit val loggingContext: ErrorLoggingContext)
extends BaseCommand[GetActiveContractsRequest, AutoCloseable, AutoCloseable]
with SubscribeBase[
GetActiveContractsRequest,
GetActiveContractsResponse,
GetActiveContractsResponse,
] {
override def createRequest(): Either[String, GetActiveContractsRequest] = {
val filter =
if (templateFilter.nonEmpty) {
Filters(
Some(
InclusiveFilters(templateFilters =
templateFilter.map(tId =>
TemplateFilter(Some(tId.toIdentifier), includeCreatedEventBlob)
)
)
)
)
} else Filters.defaultInstance
Right(
GetActiveContractsRequest(
filter = Some(TransactionFilter(parties.map((_, filter)).toMap)),
verbose = verbose,
activeAtOffset = activeAtOffset,
)
)
}
override def doRequest(
service: StateServiceStub,
request: GetActiveContractsRequest,
rawObserver: StreamObserver[GetActiveContractsResponse],
): Unit =
service.getActiveContracts(request, rawObserver)
override def extractResults(
response: GetActiveContractsResponse
): IterableOnce[GetActiveContractsResponse] = List(response)
// fetching ACS might take long if we fetch a lot of data
override def timeoutType: TimeoutType = DefaultUnboundedTimeout
}
}
final case class CompletionWrapper(
completion: Completion,
checkpoint: Checkpoint,
domainId: DomainId,
)
object CommandCompletionService {
abstract class BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = CommandCompletionServiceStub
override def createService(channel: ManagedChannel): CommandCompletionServiceStub =
CommandCompletionServiceGrpc.stub(channel)
}
final case class CompletionRequest(
partyId: LfPartyId,
beginOffset: ParticipantOffset,
expectedCompletions: Int,
timeout: java.time.Duration,
applicationId: String,
)(filter: CompletionWrapper => Boolean, scheduler: ScheduledExecutorService)
extends BaseCommand[
CompletionStreamRequest,
Seq[CompletionWrapper],
Seq[CompletionWrapper],
] {
override def createRequest(): Either[String, CompletionStreamRequest] =
Right(
CompletionStreamRequest(
applicationId = applicationId,
parties = Seq(partyId),
beginExclusive = Some(beginOffset),
)
)
override def submitRequest(
service: CommandCompletionServiceStub,
request: CompletionStreamRequest,
): Future[Seq[CompletionWrapper]] = {
import scala.jdk.DurationConverters.*
GrpcAdminCommand
.streamedResponse[CompletionStreamRequest, CompletionStreamResponse, CompletionWrapper](
service.completionStream,
response =>
List(
CompletionWrapper(
completion = response.completion.getOrElse(
throw new IllegalStateException("Completion should be present.")
),
checkpoint = response.checkpoint.getOrElse(
throw new IllegalStateException("Checkpoint should be present.")
),
domainId = DomainId.tryFromString(response.domainId),
)
).filter(filter),
request,
expectedCompletions,
timeout.toScala,
scheduler,
)
}
override def handleResponse(
response: Seq[CompletionWrapper]
): Either[String, Seq[CompletionWrapper]] =
Right(response)
override def timeoutType: TimeoutType = ServerEnforcedTimeout
}
final case class CompletionCheckpointRequest(
partyId: LfPartyId,
beginExclusive: ParticipantOffset,
expectedCompletions: Int,
timeout: config.NonNegativeDuration,
applicationId: String,
)(filter: Completion => Boolean, scheduler: ScheduledExecutorService)
extends BaseCommand[CompletionStreamRequest, Seq[(Completion, Option[Checkpoint])], Seq[
(Completion, Option[Checkpoint])
]] {
override def createRequest(): Either[String, CompletionStreamRequest] =
Right(
CompletionStreamRequest(
applicationId = applicationId,
parties = Seq(partyId),
beginExclusive = Some(beginExclusive),
)
)
override def submitRequest(
service: CommandCompletionServiceStub,
request: CompletionStreamRequest,
): Future[Seq[(Completion, Option[Checkpoint])]] = {
def extract(response: CompletionStreamResponse): Seq[(Completion, Option[Checkpoint])] = {
val checkpoint = response.checkpoint
response.completion.filter(filter).map(_ -> checkpoint).toList
}
GrpcAdminCommand.streamedResponse[
CompletionStreamRequest,
CompletionStreamResponse,
(Completion, Option[Checkpoint]),
](
service.completionStream,
extract,
request,
expectedCompletions,
timeout.asFiniteApproximation,
scheduler,
)
}
override def handleResponse(
response: Seq[(Completion, Option[Checkpoint])]
): Either[String, Seq[(Completion, Option[Checkpoint])]] =
Right(response)
override def timeoutType: TimeoutType = ServerEnforcedTimeout
}
final case class Subscribe(
observer: StreamObserver[CompletionWrapper],
parties: Seq[String],
offset: Option[ParticipantOffset],
applicationId: String,
)(implicit loggingContext: ErrorLoggingContext)
extends BaseCommand[CompletionStreamRequest, AutoCloseable, AutoCloseable] {
// The subscription should never be cut short because of a gRPC timeout
override def timeoutType: TimeoutType = ServerEnforcedTimeout
override def createRequest(): Either[String, CompletionStreamRequest] = Right {
CompletionStreamRequest(
applicationId = applicationId,
parties = parties,
beginExclusive = offset,
)
}
override def submitRequest(
service: CommandCompletionServiceStub,
request: CompletionStreamRequest,
): Future[AutoCloseable] = {
val rawObserver = new ForwardingStreamObserver[CompletionStreamResponse, CompletionWrapper](
observer,
response =>
List(
CompletionWrapper(
completion = response.completion.getOrElse(
throw new IllegalStateException("Completion should be present.")
),
checkpoint = response.checkpoint.getOrElse(
throw new IllegalStateException("Checkpoint should be present.")
),
domainId = DomainId.tryFromString(response.domainId),
)
),
)
val context = Context.current().withCancellation()
context.run(() => service.completionStream(request, rawObserver))
Future.successful(context)
}
override def handleResponse(response: AutoCloseable): Either[String, AutoCloseable] = Right(
response
)
}
}
object Time {
abstract class BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = TimeServiceStub
@ -808,23 +1615,19 @@ object LedgerApiCommands {
TimeServiceGrpc.stub(channel)
}
final case class Get(timeout: FiniteDuration)(scheduler: ScheduledExecutorService)(implicit
executionContext: ExecutionContext
) extends BaseCommand[
final object Get
extends BaseCommand[
GetTimeRequest,
Either[String, CantonTimestamp],
GetTimeResponse,
CantonTimestamp,
] {
override def submitRequest(
service: TimeServiceStub,
request: GetTimeRequest,
): Future[Either[String, CantonTimestamp]] =
service.getTime(request).map {
_.currentTime
.toRight("Empty timestamp received from ledger Api server")
.flatMap(CantonTimestamp.fromProtoTimestamp(_).leftMap(_.message))
}
): Future[GetTimeResponse] = {
service.getTime(request)
}
/** Create the request from configured options
*/
@ -833,8 +1636,12 @@ object LedgerApiCommands {
/** Handle the response the service has provided
*/
override def handleResponse(
response: Either[String, CantonTimestamp]
): Either[String, CantonTimestamp] = response
response: GetTimeResponse
): Either[String, CantonTimestamp] =
for {
prototTimestamp <- response.currentTime.map(Right(_)).getOrElse(Left("currentTime empty"))
result <- CantonTimestamp.fromProtoTimestamp(prototTimestamp).left.map(_.message)
} yield result
}
final case class Set(currentTime: CantonTimestamp, newTime: CantonTimestamp)
@ -857,9 +1664,43 @@ object LedgerApiCommands {
/** Handle the response the service has provided
*/
override def handleResponse(response: Empty): Either[String, Unit] = Either.unit
override def handleResponse(response: Empty): Either[String, Unit] = Right(())
}
}
object QueryService {
abstract class BaseCommand[Req, Res] extends GrpcAdminCommand[Req, Res, Res] {
override type Svc = EventQueryServiceStub
override def createService(channel: ManagedChannel): EventQueryServiceStub =
EventQueryServiceGrpc.stub(channel)
override def handleResponse(response: Res): Either[String, Res] = Right(response)
}
final case class GetEventsByContractId(
contractId: String,
requestingParties: Seq[String],
) extends BaseCommand[
GetEventsByContractIdRequest,
GetEventsByContractIdResponse,
] {
override def createRequest(): Either[String, GetEventsByContractIdRequest] = Right(
GetEventsByContractIdRequest(
contractId = contractId,
requestingParties = requestingParties,
)
)
override def submitRequest(
service: EventQueryServiceStub,
request: GetEventsByContractIdRequest,
): Future[GetEventsByContractIdResponse] = service.getEventsByContractId(request)
}
}
}

View File

@ -1,932 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.canton.admin.api.client.commands
import com.daml.ledger.api.v2.checkpoint.Checkpoint
import com.daml.ledger.api.v2.command_completion_service.CommandCompletionServiceGrpc.CommandCompletionServiceStub
import com.daml.ledger.api.v2.command_completion_service.{
CommandCompletionServiceGrpc,
CompletionStreamRequest,
CompletionStreamResponse,
}
import com.daml.ledger.api.v2.command_service.CommandServiceGrpc.CommandServiceStub
import com.daml.ledger.api.v2.command_service.{
CommandServiceGrpc,
SubmitAndWaitForTransactionResponse,
SubmitAndWaitForTransactionTreeResponse,
SubmitAndWaitRequest,
}
import com.daml.ledger.api.v2.command_submission_service.CommandSubmissionServiceGrpc.CommandSubmissionServiceStub
import com.daml.ledger.api.v2.command_submission_service.{
CommandSubmissionServiceGrpc,
SubmitReassignmentRequest,
SubmitReassignmentResponse,
SubmitRequest,
SubmitResponse,
}
import com.daml.ledger.api.v2.commands.{Command, Commands, DisclosedContract}
import com.daml.ledger.api.v2.completion.Completion
import com.daml.ledger.api.v2.event.CreatedEvent
import com.daml.ledger.api.v2.event_query_service.EventQueryServiceGrpc.EventQueryServiceStub
import com.daml.ledger.api.v2.event_query_service.{
EventQueryServiceGrpc,
GetEventsByContractIdRequest,
GetEventsByContractIdResponse,
}
import com.daml.ledger.api.v2.participant_offset.ParticipantOffset
import com.daml.ledger.api.v2.reassignment.{AssignedEvent, Reassignment, UnassignedEvent}
import com.daml.ledger.api.v2.reassignment_command.{
AssignCommand,
ReassignmentCommand,
UnassignCommand,
}
import com.daml.ledger.api.v2.state_service.StateServiceGrpc.StateServiceStub
import com.daml.ledger.api.v2.state_service.{
GetActiveContractsRequest,
GetActiveContractsResponse,
GetConnectedDomainsRequest,
GetConnectedDomainsResponse,
GetLedgerEndRequest,
GetLedgerEndResponse,
StateServiceGrpc,
}
import com.daml.ledger.api.v2.testing.time_service.TimeServiceGrpc.TimeServiceStub
import com.daml.ledger.api.v2.testing.time_service.{
GetTimeRequest,
GetTimeResponse,
SetTimeRequest,
TimeServiceGrpc,
}
import com.daml.ledger.api.v2.transaction.{Transaction, TransactionTree}
import com.daml.ledger.api.v2.transaction_filter.{
Filters,
InclusiveFilters,
TemplateFilter,
TransactionFilter,
}
import com.daml.ledger.api.v2.update_service.UpdateServiceGrpc.UpdateServiceStub
import com.daml.ledger.api.v2.update_service.{
GetTransactionByIdRequest,
GetTransactionTreeResponse,
GetUpdateTreesResponse,
GetUpdatesRequest,
GetUpdatesResponse,
UpdateServiceGrpc,
}
import com.digitalasset.canton.admin.api.client.commands.GrpcAdminCommand.{
DefaultUnboundedTimeout,
ServerEnforcedTimeout,
TimeoutType,
}
import com.digitalasset.canton.admin.api.client.data.TemplateId
import com.digitalasset.canton.config.RequireTypes.PositiveInt
import com.digitalasset.canton.data.CantonTimestamp
import com.digitalasset.canton.ledger.api.DeduplicationPeriod
import com.digitalasset.canton.logging.ErrorLoggingContext
import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting}
import com.digitalasset.canton.networking.grpc.ForwardingStreamObserver
import com.digitalasset.canton.protocol.LfContractId
import com.digitalasset.canton.serialization.ProtoConverter
import com.digitalasset.canton.topology.DomainId
import com.digitalasset.canton.{LfPackageId, LfPartyId, config}
import com.google.protobuf.empty.Empty
import io.grpc.*
import io.grpc.stub.StreamObserver
import java.time.Instant
import java.util.UUID
import java.util.concurrent.ScheduledExecutorService
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ExecutionContext, Future}
// TODO(#15280) delete LedgerApiCommands, and rename this to LedgerApiCommands
object LedgerApiV2Commands {
trait SubscribeBase[Req, Resp, Res] extends GrpcAdminCommand[Req, AutoCloseable, AutoCloseable] {
// The subscription should never be cut short because of a gRPC timeout
override def timeoutType: TimeoutType = ServerEnforcedTimeout
def observer: StreamObserver[Res]
def doRequest(
service: this.Svc,
request: Req,
rawObserver: StreamObserver[Resp],
): Unit
def extractResults(response: Resp): IterableOnce[Res]
implicit def loggingContext: ErrorLoggingContext
override def submitRequest(
service: this.Svc,
request: Req,
): Future[AutoCloseable] = {
val rawObserver = new ForwardingStreamObserver[Resp, Res](observer, extractResults)
val context = Context.current().withCancellation()
context.run(() => doRequest(service, request, rawObserver))
Future.successful(context)
}
override def handleResponse(response: AutoCloseable): Either[String, AutoCloseable] = Right(
response
)
}
object UpdateService {
sealed trait UpdateTreeWrapper {
def updateId: String
}
sealed trait UpdateWrapper {
def updateId: String
def createEvent: Option[CreatedEvent] =
this match {
case UpdateService.TransactionWrapper(t) => t.events.headOption.map(_.getCreated)
case u: UpdateService.AssignedWrapper => u.assignedEvent.createdEvent
case _: UpdateService.UnassignedWrapper => None
}
}
object UpdateWrapper {
def isUnassigedWrapper(wrapper: UpdateWrapper): Boolean = wrapper match {
case UnassignedWrapper(_, _) => true
case _ => false
}
}
final case class TransactionTreeWrapper(transactionTree: TransactionTree)
extends UpdateTreeWrapper {
override def updateId: String = transactionTree.updateId
}
final case class TransactionWrapper(transaction: Transaction) extends UpdateWrapper {
override def updateId: String = transaction.updateId
}
sealed trait ReassignmentWrapper extends UpdateTreeWrapper with UpdateWrapper {
def reassignment: Reassignment
def unassignId: String = reassignment.getUnassignedEvent.unassignId
def offset: ParticipantOffset = ParticipantOffset(
ParticipantOffset.Value.Absolute(reassignment.offset)
)
}
object ReassignmentWrapper {
def apply(reassignment: Reassignment): ReassignmentWrapper = {
val event = reassignment.event
event.assignedEvent
.map[ReassignmentWrapper](AssignedWrapper(reassignment, _))
.orElse(
event.unassignedEvent.map[ReassignmentWrapper](UnassignedWrapper(reassignment, _))
)
.getOrElse(
throw new IllegalStateException(
s"Invalid reassignment event (only supported UnassignedEvent and AssignedEvent): ${reassignment.event}"
)
)
}
}
final case class AssignedWrapper(reassignment: Reassignment, assignedEvent: AssignedEvent)
extends ReassignmentWrapper {
override def updateId: String = reassignment.updateId
}
final case class UnassignedWrapper(reassignment: Reassignment, unassignedEvent: UnassignedEvent)
extends ReassignmentWrapper {
override def updateId: String = reassignment.updateId
}
trait BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = UpdateServiceStub
override def createService(channel: ManagedChannel): UpdateServiceStub =
UpdateServiceGrpc.stub(channel)
}
trait SubscribeUpdateBase[Resp, Res]
extends BaseCommand[GetUpdatesRequest, AutoCloseable, AutoCloseable]
with SubscribeBase[GetUpdatesRequest, Resp, Res] {
def beginExclusive: ParticipantOffset
def endInclusive: Option[ParticipantOffset]
def filter: TransactionFilter
def verbose: Boolean
override def createRequest(): Either[String, GetUpdatesRequest] = Right {
GetUpdatesRequest(
beginExclusive = Some(beginExclusive),
endInclusive = endInclusive,
verbose = verbose,
filter = Some(filter),
)
}
}
final case class SubscribeTrees(
override val observer: StreamObserver[UpdateTreeWrapper],
override val beginExclusive: ParticipantOffset,
override val endInclusive: Option[ParticipantOffset],
override val filter: TransactionFilter,
override val verbose: Boolean,
)(override implicit val loggingContext: ErrorLoggingContext)
extends SubscribeUpdateBase[GetUpdateTreesResponse, UpdateTreeWrapper] {
override def doRequest(
service: UpdateServiceStub,
request: GetUpdatesRequest,
rawObserver: StreamObserver[GetUpdateTreesResponse],
): Unit =
service.getUpdateTrees(request, rawObserver)
override def extractResults(
response: GetUpdateTreesResponse
): IterableOnce[UpdateTreeWrapper] =
response.update.transactionTree
.map[UpdateTreeWrapper](TransactionTreeWrapper)
.orElse(response.update.reassignment.map(ReassignmentWrapper(_)))
}
final case class SubscribeFlat(
override val observer: StreamObserver[UpdateWrapper],
override val beginExclusive: ParticipantOffset,
override val endInclusive: Option[ParticipantOffset],
override val filter: TransactionFilter,
override val verbose: Boolean,
)(override implicit val loggingContext: ErrorLoggingContext)
extends SubscribeUpdateBase[GetUpdatesResponse, UpdateWrapper] {
override def doRequest(
service: UpdateServiceStub,
request: GetUpdatesRequest,
rawObserver: StreamObserver[GetUpdatesResponse],
): Unit =
service.getUpdates(request, rawObserver)
override def extractResults(response: GetUpdatesResponse): IterableOnce[UpdateWrapper] =
response.update.transaction
.map[UpdateWrapper](TransactionWrapper)
.orElse(response.update.reassignment.map(ReassignmentWrapper(_)))
}
final case class GetTransactionById(parties: Set[LfPartyId], id: String)(implicit
ec: ExecutionContext
) extends BaseCommand[GetTransactionByIdRequest, GetTransactionTreeResponse, Option[
TransactionTree
]]
with PrettyPrinting {
override def createRequest(): Either[String, GetTransactionByIdRequest] = Right {
GetTransactionByIdRequest(
updateId = id,
requestingParties = parties.toSeq,
)
}
override def submitRequest(
service: UpdateServiceStub,
request: GetTransactionByIdRequest,
): Future[GetTransactionTreeResponse] = {
// The Ledger API will throw an error if it can't find a transaction by ID.
// However, as Canton is distributed, a transaction ID might show up later, so we don't treat this as
// an error and change it to a None
service.getTransactionTreeById(request).recover {
case e: StatusRuntimeException if e.getStatus.getCode == Status.Code.NOT_FOUND =>
GetTransactionTreeResponse(None)
}
}
override def handleResponse(
response: GetTransactionTreeResponse
): Either[String, Option[TransactionTree]] =
Right(response.transaction)
override def pretty: Pretty[GetTransactionById] =
prettyOfClass(
param("id", _.id.unquoted),
param("parties", _.parties),
)
}
}
private[commands] trait SubmitCommand extends PrettyPrinting {
def actAs: Seq[LfPartyId]
def readAs: Seq[LfPartyId]
def commands: Seq[Command]
def workflowId: String
def commandId: String
def deduplicationPeriod: Option[DeduplicationPeriod]
def submissionId: String
def minLedgerTimeAbs: Option[Instant]
def disclosedContracts: Seq[DisclosedContract]
def domainId: Option[DomainId]
def applicationId: String
def packageIdSelectionPreference: Seq[LfPackageId]
protected def mkCommand: Commands = Commands(
workflowId = workflowId,
applicationId = applicationId,
commandId = if (commandId.isEmpty) UUID.randomUUID().toString else commandId,
actAs = actAs,
readAs = readAs,
commands = commands,
deduplicationPeriod = deduplicationPeriod.fold(
Commands.DeduplicationPeriod.Empty: Commands.DeduplicationPeriod
) {
case DeduplicationPeriod.DeduplicationDuration(duration) =>
Commands.DeduplicationPeriod.DeduplicationDuration(
ProtoConverter.DurationConverter.toProtoPrimitive(duration)
)
case DeduplicationPeriod.DeduplicationOffset(offset) =>
Commands.DeduplicationPeriod.DeduplicationOffset(
offset.toHexString
)
},
minLedgerTimeAbs = minLedgerTimeAbs.map(ProtoConverter.InstantConverter.toProtoPrimitive),
submissionId = submissionId,
disclosedContracts = disclosedContracts,
domainId = domainId.map(_.toProtoPrimitive).getOrElse(""),
packageIdSelectionPreference = packageIdSelectionPreference.map(_.toString),
)
override def pretty: Pretty[this.type] =
prettyOfClass(
param("actAs", _.actAs),
param("readAs", _.readAs),
param("commandId", _.commandId.singleQuoted),
param("workflowId", _.workflowId.singleQuoted),
param("submissionId", _.submissionId.singleQuoted),
param("deduplicationPeriod", _.deduplicationPeriod),
paramIfDefined("minLedgerTimeAbs", _.minLedgerTimeAbs),
paramWithoutValue("commands"),
)
}
object CommandSubmissionService {
trait BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = CommandSubmissionServiceStub
override def createService(channel: ManagedChannel): CommandSubmissionServiceStub =
CommandSubmissionServiceGrpc.stub(channel)
}
final case class Submit(
override val actAs: Seq[LfPartyId],
override val readAs: Seq[LfPartyId],
override val commands: Seq[Command],
override val workflowId: String,
override val commandId: String,
override val deduplicationPeriod: Option[DeduplicationPeriod],
override val submissionId: String,
override val minLedgerTimeAbs: Option[Instant],
override val disclosedContracts: Seq[DisclosedContract],
override val domainId: Option[DomainId],
override val applicationId: String,
override val packageIdSelectionPreference: Seq[LfPackageId],
) extends SubmitCommand
with BaseCommand[SubmitRequest, SubmitResponse, Unit] {
override def createRequest(): Either[String, SubmitRequest] = Right(
SubmitRequest(commands = Some(mkCommand))
)
override def submitRequest(
service: CommandSubmissionServiceStub,
request: SubmitRequest,
): Future[SubmitResponse] = {
service.submit(request)
}
override def handleResponse(response: SubmitResponse): Either[String, Unit] = Right(())
}
final case class SubmitAssignCommand(
workflowId: String,
applicationId: String,
commandId: String,
submitter: LfPartyId,
submissionId: String,
unassignId: String,
source: DomainId,
target: DomainId,
) extends BaseCommand[SubmitReassignmentRequest, SubmitReassignmentResponse, Unit] {
override def createRequest(): Either[String, SubmitReassignmentRequest] = Right(
SubmitReassignmentRequest(
Some(
ReassignmentCommand(
workflowId = workflowId,
applicationId = applicationId,
commandId = commandId,
submitter = submitter.toString,
command = ReassignmentCommand.Command.AssignCommand(
AssignCommand(
unassignId = unassignId,
source = source.toProtoPrimitive,
target = target.toProtoPrimitive,
)
),
submissionId = submissionId,
)
)
)
)
override def submitRequest(
service: CommandSubmissionServiceStub,
request: SubmitReassignmentRequest,
): Future[SubmitReassignmentResponse] = {
service.submitReassignment(request)
}
override def handleResponse(response: SubmitReassignmentResponse): Either[String, Unit] =
Right(())
}
final case class SubmitUnassignCommand(
workflowId: String,
applicationId: String,
commandId: String,
submitter: LfPartyId,
submissionId: String,
contractId: LfContractId,
source: DomainId,
target: DomainId,
) extends BaseCommand[SubmitReassignmentRequest, SubmitReassignmentResponse, Unit] {
override def createRequest(): Either[String, SubmitReassignmentRequest] = Right(
SubmitReassignmentRequest(
Some(
ReassignmentCommand(
workflowId = workflowId,
applicationId = applicationId,
commandId = commandId,
submitter = submitter.toString,
command = ReassignmentCommand.Command.UnassignCommand(
UnassignCommand(
contractId = contractId.coid.toString,
source = source.toProtoPrimitive,
target = target.toProtoPrimitive,
)
),
submissionId = submissionId,
)
)
)
)
override def submitRequest(
service: CommandSubmissionServiceStub,
request: SubmitReassignmentRequest,
): Future[SubmitReassignmentResponse] = {
service.submitReassignment(request)
}
override def handleResponse(response: SubmitReassignmentResponse): Either[String, Unit] =
Right(())
}
}
object CommandService {
trait BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = CommandServiceStub
override def createService(channel: ManagedChannel): CommandServiceStub =
CommandServiceGrpc.stub(channel)
}
final case class SubmitAndWaitTransactionTree(
override val actAs: Seq[LfPartyId],
override val readAs: Seq[LfPartyId],
override val commands: Seq[Command],
override val workflowId: String,
override val commandId: String,
override val deduplicationPeriod: Option[DeduplicationPeriod],
override val submissionId: String,
override val minLedgerTimeAbs: Option[Instant],
override val disclosedContracts: Seq[DisclosedContract],
override val domainId: Option[DomainId],
override val applicationId: String,
override val packageIdSelectionPreference: Seq[LfPackageId],
) extends SubmitCommand
with BaseCommand[
SubmitAndWaitRequest,
SubmitAndWaitForTransactionTreeResponse,
TransactionTree,
] {
override def createRequest(): Either[String, SubmitAndWaitRequest] =
Right(SubmitAndWaitRequest(commands = Some(mkCommand)))
override def submitRequest(
service: CommandServiceStub,
request: SubmitAndWaitRequest,
): Future[SubmitAndWaitForTransactionTreeResponse] =
service.submitAndWaitForTransactionTree(request)
override def handleResponse(
response: SubmitAndWaitForTransactionTreeResponse
): Either[String, TransactionTree] =
response.transaction.toRight("Received response without any transaction tree")
override def timeoutType: TimeoutType = DefaultUnboundedTimeout
}
final case class SubmitAndWaitTransaction(
override val actAs: Seq[LfPartyId],
override val readAs: Seq[LfPartyId],
override val commands: Seq[Command],
override val workflowId: String,
override val commandId: String,
override val deduplicationPeriod: Option[DeduplicationPeriod],
override val submissionId: String,
override val minLedgerTimeAbs: Option[Instant],
override val disclosedContracts: Seq[DisclosedContract],
override val domainId: Option[DomainId],
override val applicationId: String,
override val packageIdSelectionPreference: Seq[LfPackageId],
) extends SubmitCommand
with BaseCommand[SubmitAndWaitRequest, SubmitAndWaitForTransactionResponse, Transaction] {
override def createRequest(): Either[String, SubmitAndWaitRequest] =
Right(SubmitAndWaitRequest(commands = Some(mkCommand)))
override def submitRequest(
service: CommandServiceStub,
request: SubmitAndWaitRequest,
): Future[SubmitAndWaitForTransactionResponse] =
service.submitAndWaitForTransaction(request)
override def handleResponse(
response: SubmitAndWaitForTransactionResponse
): Either[String, Transaction] =
response.transaction.toRight("Received response without any transaction")
override def timeoutType: TimeoutType = DefaultUnboundedTimeout
}
}
object StateService {
abstract class BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = StateServiceStub
override def createService(channel: ManagedChannel): StateServiceStub =
StateServiceGrpc.stub(channel)
}
final case class LedgerEnd()
extends BaseCommand[GetLedgerEndRequest, GetLedgerEndResponse, ParticipantOffset] {
override def createRequest(): Either[String, GetLedgerEndRequest] =
Right(GetLedgerEndRequest())
override def submitRequest(
service: StateServiceStub,
request: GetLedgerEndRequest,
): Future[GetLedgerEndResponse] =
service.getLedgerEnd(request)
override def handleResponse(
response: GetLedgerEndResponse
): Either[String, ParticipantOffset] =
response.offset.toRight("Empty LedgerEndResponse received without offset")
}
final case class GetConnectedDomains(partyId: LfPartyId)
extends BaseCommand[
GetConnectedDomainsRequest,
GetConnectedDomainsResponse,
GetConnectedDomainsResponse,
] {
override def createRequest(): Either[String, GetConnectedDomainsRequest] =
Right(GetConnectedDomainsRequest(partyId.toString))
override def submitRequest(
service: StateServiceStub,
request: GetConnectedDomainsRequest,
): Future[GetConnectedDomainsResponse] =
service.getConnectedDomains(request)
override def handleResponse(
response: GetConnectedDomainsResponse
): Either[String, GetConnectedDomainsResponse] =
Right(response)
}
final case class GetActiveContracts(
observer: StreamObserver[GetActiveContractsResponse],
parties: Set[LfPartyId],
limit: PositiveInt,
templateFilter: Seq[TemplateId] = Seq.empty,
activeAtOffset: String = "",
verbose: Boolean = true,
timeout: FiniteDuration,
includeCreatedEventBlob: Boolean = false,
)(override implicit val loggingContext: ErrorLoggingContext)
extends BaseCommand[GetActiveContractsRequest, AutoCloseable, AutoCloseable]
with SubscribeBase[
GetActiveContractsRequest,
GetActiveContractsResponse,
GetActiveContractsResponse,
] {
override def createRequest(): Either[String, GetActiveContractsRequest] = {
val filter =
if (templateFilter.nonEmpty) {
Filters(
Some(
InclusiveFilters(templateFilters =
templateFilter.map(tId =>
TemplateFilter(Some(tId.toIdentifier), includeCreatedEventBlob)
)
)
)
)
} else Filters.defaultInstance
Right(
GetActiveContractsRequest(
filter = Some(TransactionFilter(parties.map((_, filter)).toMap)),
verbose = verbose,
activeAtOffset = activeAtOffset,
)
)
}
override def doRequest(
service: StateServiceStub,
request: GetActiveContractsRequest,
rawObserver: StreamObserver[GetActiveContractsResponse],
): Unit =
service.getActiveContracts(request, rawObserver)
override def extractResults(
response: GetActiveContractsResponse
): IterableOnce[GetActiveContractsResponse] = List(response)
// fetching ACS might take long if we fetch a lot of data
override def timeoutType: TimeoutType = DefaultUnboundedTimeout
}
}
final case class CompletionWrapper(
completion: Completion,
checkpoint: Checkpoint,
domainId: DomainId,
)
object CommandCompletionService {
abstract class BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = CommandCompletionServiceStub
override def createService(channel: ManagedChannel): CommandCompletionServiceStub =
CommandCompletionServiceGrpc.stub(channel)
}
final case class CompletionRequest(
partyId: LfPartyId,
beginOffset: ParticipantOffset,
expectedCompletions: Int,
timeout: java.time.Duration,
applicationId: String,
)(filter: CompletionWrapper => Boolean, scheduler: ScheduledExecutorService)
extends BaseCommand[
CompletionStreamRequest,
Seq[CompletionWrapper],
Seq[CompletionWrapper],
] {
override def createRequest(): Either[String, CompletionStreamRequest] =
Right(
CompletionStreamRequest(
applicationId = applicationId,
parties = Seq(partyId),
beginExclusive = Some(beginOffset),
)
)
override def submitRequest(
service: CommandCompletionServiceStub,
request: CompletionStreamRequest,
): Future[Seq[CompletionWrapper]] = {
import scala.jdk.DurationConverters.*
GrpcAdminCommand
.streamedResponse[CompletionStreamRequest, CompletionStreamResponse, CompletionWrapper](
service.completionStream,
response =>
List(
CompletionWrapper(
completion = response.completion.getOrElse(
throw new IllegalStateException("Completion should be present.")
),
checkpoint = response.checkpoint.getOrElse(
throw new IllegalStateException("Checkpoint should be present.")
),
domainId = DomainId.tryFromString(response.domainId),
)
).filter(filter),
request,
expectedCompletions,
timeout.toScala,
scheduler,
)
}
override def handleResponse(
response: Seq[CompletionWrapper]
): Either[String, Seq[CompletionWrapper]] =
Right(response)
override def timeoutType: TimeoutType = ServerEnforcedTimeout
}
final case class CompletionCheckpointRequest(
partyId: LfPartyId,
beginExclusive: ParticipantOffset,
expectedCompletions: Int,
timeout: config.NonNegativeDuration,
applicationId: String,
)(filter: Completion => Boolean, scheduler: ScheduledExecutorService)
extends BaseCommand[CompletionStreamRequest, Seq[(Completion, Option[Checkpoint])], Seq[
(Completion, Option[Checkpoint])
]] {
override def createRequest(): Either[String, CompletionStreamRequest] =
Right(
CompletionStreamRequest(
applicationId = applicationId,
parties = Seq(partyId),
beginExclusive = Some(beginExclusive),
)
)
override def submitRequest(
service: CommandCompletionServiceStub,
request: CompletionStreamRequest,
): Future[Seq[(Completion, Option[Checkpoint])]] = {
def extract(response: CompletionStreamResponse): Seq[(Completion, Option[Checkpoint])] = {
val checkpoint = response.checkpoint
response.completion.filter(filter).map(_ -> checkpoint).toList
}
GrpcAdminCommand.streamedResponse[
CompletionStreamRequest,
CompletionStreamResponse,
(Completion, Option[Checkpoint]),
](
service.completionStream,
extract,
request,
expectedCompletions,
timeout.asFiniteApproximation,
scheduler,
)
}
override def handleResponse(
response: Seq[(Completion, Option[Checkpoint])]
): Either[String, Seq[(Completion, Option[Checkpoint])]] =
Right(response)
override def timeoutType: TimeoutType = ServerEnforcedTimeout
}
final case class Subscribe(
observer: StreamObserver[CompletionWrapper],
parties: Seq[String],
offset: Option[ParticipantOffset],
applicationId: String,
)(implicit loggingContext: ErrorLoggingContext)
extends BaseCommand[CompletionStreamRequest, AutoCloseable, AutoCloseable] {
// The subscription should never be cut short because of a gRPC timeout
override def timeoutType: TimeoutType = ServerEnforcedTimeout
override def createRequest(): Either[String, CompletionStreamRequest] = Right {
CompletionStreamRequest(
applicationId = applicationId,
parties = parties,
beginExclusive = offset,
)
}
override def submitRequest(
service: CommandCompletionServiceStub,
request: CompletionStreamRequest,
): Future[AutoCloseable] = {
val rawObserver = new ForwardingStreamObserver[CompletionStreamResponse, CompletionWrapper](
observer,
response =>
List(
CompletionWrapper(
completion = response.completion.getOrElse(
throw new IllegalStateException("Completion should be present.")
),
checkpoint = response.checkpoint.getOrElse(
throw new IllegalStateException("Checkpoint should be present.")
),
domainId = DomainId.tryFromString(response.domainId),
)
),
)
val context = Context.current().withCancellation()
context.run(() => service.completionStream(request, rawObserver))
Future.successful(context)
}
override def handleResponse(response: AutoCloseable): Either[String, AutoCloseable] = Right(
response
)
}
}
object Time {
abstract class BaseCommand[Req, Resp, Res] extends GrpcAdminCommand[Req, Resp, Res] {
override type Svc = TimeServiceStub
override def createService(channel: ManagedChannel): TimeServiceStub =
TimeServiceGrpc.stub(channel)
}
final object Get
extends BaseCommand[
GetTimeRequest,
GetTimeResponse,
CantonTimestamp,
] {
override def submitRequest(
service: TimeServiceStub,
request: GetTimeRequest,
): Future[GetTimeResponse] = {
service.getTime(request)
}
/** Create the request from configured options
*/
override def createRequest(): Either[String, GetTimeRequest] = Right(GetTimeRequest())
/** Handle the response the service has provided
*/
override def handleResponse(
response: GetTimeResponse
): Either[String, CantonTimestamp] =
for {
prototTimestamp <- response.currentTime.map(Right(_)).getOrElse(Left("currentTime empty"))
result <- CantonTimestamp.fromProtoTimestamp(prototTimestamp).left.map(_.message)
} yield result
}
final case class Set(currentTime: CantonTimestamp, newTime: CantonTimestamp)
extends BaseCommand[
SetTimeRequest,
Empty,
Unit,
] {
override def submitRequest(service: TimeServiceStub, request: SetTimeRequest): Future[Empty] =
service.setTime(request)
override def createRequest(): Either[String, SetTimeRequest] =
Right(
SetTimeRequest(
currentTime = Some(currentTime.toProtoTimestamp),
newTime = Some(newTime.toProtoTimestamp),
)
)
/** Handle the response the service has provided
*/
override def handleResponse(response: Empty): Either[String, Unit] = Right(())
}
}
object QueryService {
abstract class BaseCommand[Req, Res] extends GrpcAdminCommand[Req, Res, Res] {
override type Svc = EventQueryServiceStub
override def createService(channel: ManagedChannel): EventQueryServiceStub =
EventQueryServiceGrpc.stub(channel)
override def handleResponse(response: Res): Either[String, Res] = Right(response)
}
final case class GetEventsByContractId(
contractId: String,
requestingParties: Seq[String],
) extends BaseCommand[
GetEventsByContractIdRequest,
GetEventsByContractIdResponse,
] {
override def createRequest(): Either[String, GetEventsByContractIdRequest] = Right(
GetEventsByContractIdRequest(
contractId = contractId,
requestingParties = requestingParties,
)
)
override def submitRequest(
service: EventQueryServiceStub,
request: GetEventsByContractIdRequest,
): Future[GetEventsByContractIdResponse] = service.getEventsByContractId(request)
}
}
}

View File

@ -43,16 +43,15 @@ import com.daml.lf.data.Ref
import com.daml.metrics.api.MetricHandle.{Histogram, Meter}
import com.daml.metrics.api.{MetricName, MetricsContext}
import com.daml.scalautil.Statement.discard
import com.digitalasset.canton.admin.api.client.commands.LedgerApiCommands.CompletionWrapper
import com.digitalasset.canton.admin.api.client.commands.LedgerApiCommands.UpdateService.*
import com.digitalasset.canton.admin.api.client.commands.LedgerApiTypeWrappers.{
WrappedContractEntry,
WrappedIncompleteAssigned,
WrappedIncompleteUnassigned,
}
import com.digitalasset.canton.admin.api.client.commands.LedgerApiV2Commands.CompletionWrapper
import com.digitalasset.canton.admin.api.client.commands.LedgerApiV2Commands.UpdateService.*
import com.digitalasset.canton.admin.api.client.commands.{
LedgerApiCommands,
LedgerApiV2Commands,
ParticipantAdminCommands,
}
import com.digitalasset.canton.admin.api.client.data.*
@ -220,7 +219,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
check(FeatureFlag.Testing)(
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.UpdateService.SubscribeTrees(
LedgerApiCommands.UpdateService.SubscribeTrees(
observer,
beginOffset,
endOffset,
@ -311,7 +310,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
check(FeatureFlag.Testing)(
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.UpdateService.SubscribeFlat(
LedgerApiCommands.UpdateService.SubscribeFlat(
observer,
beginOffset,
endOffset,
@ -404,7 +403,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
def by_id(parties: Set[PartyId], id: String): Option[TransactionTreeProto] =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.UpdateService.GetTransactionById(parties.map(_.toLf), id)(
LedgerApiCommands.UpdateService.GetTransactionById(parties.map(_.toLf), id)(
consoleEnvironment.environment.executionContext
)
)
@ -442,7 +441,6 @@ trait BaseLedgerApiAdministration extends NoTracing {
domainId: Option[DomainId] = None,
workflowId: String = "",
commandId: String = "",
// TODO(#15280) This feature wont work after V1 is removed. Also after witness blinding is implemented, the underlying algorith will be broken. Idea: drop this feature and wait explicitly with some additional tooling.
optTimeout: Option[config.NonNegativeDuration] = Some(timeouts.ledgerCommand),
deduplicationPeriod: Option[DeduplicationPeriod] = None,
submissionId: String = "",
@ -454,7 +452,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): TransactionTreeProto = {
val tx = consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandService.SubmitAndWaitTransactionTree(
LedgerApiCommands.CommandService.SubmitAndWaitTransactionTree(
actAs.map(_.toLf),
readAs.map(_.toLf),
commands,
@ -493,7 +491,6 @@ trait BaseLedgerApiAdministration extends NoTracing {
domainId: Option[DomainId] = None,
workflowId: String = "",
commandId: String = "",
// TODO(#15280) This feature wont work after V1 is removed. Also after witness blinding is implemented, the underlying algorith will be broken. Idea: drop this feature and wait explicitly with some additional tooling.
optTimeout: Option[config.NonNegativeDuration] = Some(timeouts.ledgerCommand),
deduplicationPeriod: Option[DeduplicationPeriod] = None,
submissionId: String = "",
@ -505,7 +502,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): TransactionV2 = {
val tx = consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandService.SubmitAndWaitTransaction(
LedgerApiCommands.CommandService.SubmitAndWaitTransaction(
actAs.map(_.toLf),
readAs.map(_.toLf),
commands,
@ -545,7 +542,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): Unit = check(FeatureFlag.Testing) {
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandSubmissionService.Submit(
LedgerApiCommands.CommandSubmissionService.Submit(
actAs.map(_.toLf),
readAs.map(_.toLf),
commands,
@ -739,7 +736,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): Unit = check(FeatureFlag.Testing) {
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandSubmissionService.SubmitAssignCommand(
LedgerApiCommands.CommandSubmissionService.SubmitAssignCommand(
workflowId = workflowId,
applicationId = applicationId,
commandId = commandId,
@ -770,7 +767,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): Unit = check(FeatureFlag.Testing) {
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandSubmissionService.SubmitUnassignCommand(
LedgerApiCommands.CommandSubmissionService.SubmitUnassignCommand(
workflowId = workflowId,
applicationId = applicationId,
commandId = commandId,
@ -793,7 +790,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
def end(): ParticipantOffset =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.StateService.LedgerEnd()
LedgerApiCommands.StateService.LedgerEnd()
)
})
@ -801,7 +798,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
def connected_domains(partyId: PartyId): GetConnectedDomainsResponse =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.StateService.GetConnectedDomains(partyId.toLf)
LedgerApiCommands.StateService.GetConnectedDomains(partyId.toLf)
)
})
@ -837,7 +834,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
mkResult(
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.StateService.GetActiveContracts(
LedgerApiCommands.StateService.GetActiveContracts(
observer,
Set(party.toLf),
limit,
@ -1005,7 +1002,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
mkResult(
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.StateService.GetActiveContracts(
LedgerApiCommands.StateService.GetActiveContracts(
observer,
localParties.toSet,
limit,
@ -1237,7 +1234,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): Seq[CompletionWrapper] =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandCompletionService.CompletionRequest(
LedgerApiCommands.CommandCompletionService.CompletionRequest(
partyId.toLf,
beginOffset,
atLeastNumCompletions,
@ -1265,7 +1262,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): Seq[(Completion, Option[Checkpoint])] =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandCompletionService.CompletionCheckpointRequest(
LedgerApiCommands.CommandCompletionService.CompletionCheckpointRequest(
partyId.toLf,
beginExclusive,
atLeastNumCompletions,
@ -1295,7 +1292,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
check(FeatureFlag.Testing)(
consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandCompletionService.Subscribe(
LedgerApiCommands.CommandCompletionService.Subscribe(
observer,
parties.map(_.toLf),
Some(beginOffset),
@ -1700,7 +1697,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
def get(): CantonTimestamp =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.Time.Get
LedgerApiCommands.Time.Get
)
})
@ -1710,7 +1707,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
)
def set(currentTime: CantonTimestamp, nextTime: CantonTimestamp): Unit =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(LedgerApiV2Commands.Time.Set(currentTime, nextTime))
ledgerApiCommand(LedgerApiCommands.Time.Set(currentTime, nextTime))
})
}
@ -1727,7 +1724,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): GetEventsByContractIdResponse =
check(FeatureFlag.Testing)(consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.QueryService
LedgerApiCommands.QueryService
.GetEventsByContractId(contractId, requestingParties.map(_.toLf))
)
})
@ -1760,7 +1757,6 @@ trait BaseLedgerApiAdministration extends NoTracing {
domainId: Option[DomainId] = None,
workflowId: String = "",
commandId: String = "",
// TODO(#15280) This feature wont work after V1 is removed. Also after witness blinding is implemented, the underlying algorith will be broken. Idea: drop this feature and wait explicitly with some additional tooling.
optTimeout: Option[config.NonNegativeDuration] = Some(timeouts.ledgerCommand),
deduplicationPeriod: Option[DeduplicationPeriod] = None,
submissionId: String = "",
@ -1772,7 +1768,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): TransactionTree = check(FeatureFlag.Testing) {
val tx = consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandService.SubmitAndWaitTransactionTree(
LedgerApiCommands.CommandService.SubmitAndWaitTransactionTree(
actAs.map(_.toLf),
readAs.map(_.toLf),
commands.map(c => Command.fromJavaProto(c.toProtoCommand)),
@ -1814,7 +1810,6 @@ trait BaseLedgerApiAdministration extends NoTracing {
domainId: Option[DomainId] = None,
workflowId: String = "",
commandId: String = "",
// TODO(#15280) This feature wont work after V1 is removed. Also after witness blinding is implemented, the underlying algorith will be broken. Idea: drop this feature and wait explicitly with some additional tooling.
optTimeout: Option[config.NonNegativeDuration] = Some(timeouts.ledgerCommand),
deduplicationPeriod: Option[DeduplicationPeriod] = None,
submissionId: String = "",
@ -1826,7 +1821,7 @@ trait BaseLedgerApiAdministration extends NoTracing {
): Transaction = check(FeatureFlag.Testing) {
val tx = consoleEnvironment.run {
ledgerApiCommand(
LedgerApiV2Commands.CommandService.SubmitAndWaitTransaction(
LedgerApiCommands.CommandService.SubmitAndWaitTransaction(
actAs.map(_.toLf),
readAs.map(_.toLf),
commands.map(c => Command.fromJavaProto(c.toProtoCommand)),

View File

@ -547,7 +547,7 @@ class ParticipantPruningAdministrationGroup(
def find_safe_offset(beforeOrAt: Instant = Instant.now()): Option[ParticipantOffset] = {
check(FeatureFlag.Preview) {
val ledgerEnd = consoleEnvironment.run(
ledgerApiCommand(LedgerApiV2Commands.StateService.LedgerEnd())
ledgerApiCommand(LedgerApiCommands.StateService.LedgerEnd())
)
consoleEnvironment
.run(

View File

@ -219,6 +219,9 @@ trait PrettyInstances {
implicit def prettyLfIdentifier: Pretty[com.daml.lf.data.Ref.Identifier] =
prettyOfString(id => show"${id.packageId}:${id.qualifiedName}")
implicit def prettyLfPackageName: Pretty[com.daml.lf.data.Ref.PackageName] =
prettyOfString(packageName => show"${packageName.toString}")
implicit def prettyLfContractId: Pretty[LfContractId] = prettyOfString {
case LfContractId.V1(discriminator, suffix)
// Shorten only Canton contract ids

View File

@ -22,6 +22,7 @@ import com.digitalasset.canton.{
LedgerParticipantId,
LedgerSubmissionId,
LedgerTransactionId,
LfPackageName,
LfPartyId,
LfWorkflowId,
ProtoDeserializationError,
@ -153,6 +154,9 @@ object ProtoConverter {
def parseTemplateId(id: String): ParsingResult[LfTemplateId] =
parseString(id)(LfTemplateId.fromString)
def parseLfPackageName(packageName: String): ParsingResult[LfPackageName] =
parseString(packageName)(LfPackageName.fromString)
private def parseString[T](from: String)(to: String => Either[String, T]): ParsingResult[T] =
to(from).leftMap(StringConversionError)
object InstantConverter extends ProtoConverter[Instant, Timestamp, ProtoDeserializationError] {

View File

@ -16,16 +16,20 @@ public final class ArchivedEvent implements Event {
private final Identifier templateId;
private final String packageName;
private final String contractId;
public ArchivedEvent(
@NonNull List<@NonNull String> witnessParties,
@NonNull String eventId,
@NonNull Identifier templateId,
@NonNull String packageName,
@NonNull String contractId) {
this.witnessParties = witnessParties;
this.eventId = eventId;
this.templateId = templateId;
this.packageName = packageName;
this.contractId = contractId;
}
@ -47,6 +51,12 @@ public final class ArchivedEvent implements Event {
return templateId;
}
@NonNull
@Override
public String getPackageName() {
return packageName;
}
@NonNull
@Override
public String getContractId() {
@ -61,13 +71,13 @@ public final class ArchivedEvent implements Event {
return Objects.equals(witnessParties, that.witnessParties)
&& Objects.equals(eventId, that.eventId)
&& Objects.equals(templateId, that.templateId)
&& Objects.equals(packageName, that.packageName)
&& Objects.equals(contractId, that.contractId);
}
@Override
public int hashCode() {
return Objects.hash(witnessParties, eventId, templateId, contractId);
return Objects.hash(witnessParties, eventId, templateId, packageName, contractId);
}
@Override
@ -78,6 +88,8 @@ public final class ArchivedEvent implements Event {
+ ", eventId='"
+ eventId
+ '\''
+ ", packageName="
+ packageName
+ ", templateId="
+ templateId
+ ", contractId='"
@ -91,6 +103,7 @@ public final class ArchivedEvent implements Event {
.setContractId(getContractId())
.setEventId(getEventId())
.setTemplateId(getTemplateId().toProto())
.setPackageName(getPackageName())
.addAllWitnessParties(this.getWitnessParties())
.build();
}
@ -100,6 +113,7 @@ public final class ArchivedEvent implements Event {
archivedEvent.getWitnessPartiesList(),
archivedEvent.getEventId(),
Identifier.fromProto(archivedEvent.getTemplateId()),
archivedEvent.getPackageName(),
archivedEvent.getContractId());
}
}

View File

@ -92,6 +92,7 @@ public final class CreatedEvent implements Event, TreeEvent {
}
@NonNull
@Override
public String getPackageName() {
return packageName;
}

View File

@ -25,6 +25,9 @@ public interface Event {
@NonNull
Identifier getTemplateId();
@NonNull
String getPackageName();
@NonNull
String getContractId();

View File

@ -17,6 +17,8 @@ public final class ExercisedEvent implements TreeEvent {
private final Identifier templateId;
private final String packageName;
private final Optional<Identifier> interfaceId;
private final String contractId;
@ -37,6 +39,7 @@ public final class ExercisedEvent implements TreeEvent {
@NonNull List<@NonNull String> witnessParties,
@NonNull String eventId,
@NonNull Identifier templateId,
@NonNull String packageName,
@NonNull Optional<Identifier> interfaceId,
@NonNull String contractId,
@NonNull String choice,
@ -48,6 +51,7 @@ public final class ExercisedEvent implements TreeEvent {
this.witnessParties = witnessParties;
this.eventId = eventId;
this.templateId = templateId;
this.packageName = packageName;
this.interfaceId = interfaceId;
this.contractId = contractId;
this.choice = choice;
@ -76,6 +80,12 @@ public final class ExercisedEvent implements TreeEvent {
return templateId;
}
@NonNull
@Override
public String getPackageName() {
return packageName;
}
@NonNull
public Optional<Identifier> getInterfaceId() {
return interfaceId;
@ -124,6 +134,7 @@ public final class ExercisedEvent implements TreeEvent {
&& Objects.equals(witnessParties, that.witnessParties)
&& Objects.equals(eventId, that.eventId)
&& Objects.equals(templateId, that.templateId)
&& Objects.equals(packageName, that.packageName)
&& Objects.equals(interfaceId, that.interfaceId)
&& Objects.equals(contractId, that.contractId)
&& Objects.equals(choice, that.choice)
@ -140,6 +151,7 @@ public final class ExercisedEvent implements TreeEvent {
witnessParties,
eventId,
templateId,
packageName,
interfaceId,
contractId,
choice,
@ -160,6 +172,8 @@ public final class ExercisedEvent implements TreeEvent {
+ '\''
+ ", templateId="
+ templateId
+ ", packageName="
+ packageName
+ ", interfaceId="
+ interfaceId
+ ", contractId='"
@ -189,6 +203,7 @@ public final class ExercisedEvent implements TreeEvent {
builder.setConsuming(isConsuming());
builder.setContractId(getContractId());
builder.setTemplateId(getTemplateId().toProto());
builder.setPackageName(getPackageName());
interfaceId.ifPresent(i -> builder.setInterfaceId(i.toProto()));
builder.addAllActingParties(getActingParties());
builder.addAllWitnessParties(getWitnessParties());
@ -202,6 +217,7 @@ public final class ExercisedEvent implements TreeEvent {
exercisedEvent.getWitnessPartiesList(),
exercisedEvent.getEventId(),
Identifier.fromProto(exercisedEvent.getTemplateId()),
exercisedEvent.getPackageName(),
exercisedEvent.hasInterfaceId()
? Optional.of(Identifier.fromProto(exercisedEvent.getInterfaceId()))
: Optional.empty(),

View File

@ -25,6 +25,9 @@ public interface TreeEvent {
@NonNull
Identifier getTemplateId();
@NonNull
String getPackageName();
@NonNull
String getContractId();

View File

@ -17,6 +17,8 @@ public final class UnassignedEvent {
private final @NonNull Identifier templateId;
private final @NonNull String packageName;
private final @NonNull String source;
private final @NonNull String target;
@ -33,6 +35,7 @@ public final class UnassignedEvent {
@NonNull String unassignId,
@NonNull String contractId,
@NonNull Identifier templateId,
@NonNull String packageName,
@NonNull String source,
@NonNull String target,
@NonNull String submitter,
@ -42,6 +45,7 @@ public final class UnassignedEvent {
this.unassignId = unassignId;
this.contractId = contractId;
this.templateId = templateId;
this.packageName = packageName;
this.source = source;
this.target = target;
this.submitter = submitter;
@ -65,6 +69,11 @@ public final class UnassignedEvent {
return templateId;
}
@NonNull
public String getPackageName() {
return packageName;
}
@NonNull
public String getSource() {
return source;
@ -100,6 +109,7 @@ public final class UnassignedEvent {
UnassignedEvent that = (UnassignedEvent) o;
return Objects.equals(unassignId, that.unassignId)
&& Objects.equals(contractId, that.contractId)
&& Objects.equals(packageName, that.packageName)
&& Objects.equals(templateId, that.templateId)
&& Objects.equals(source, that.source)
&& Objects.equals(target, that.target)
@ -115,6 +125,7 @@ public final class UnassignedEvent {
unassignId,
contractId,
templateId,
packageName,
source,
target,
submitter,
@ -132,6 +143,8 @@ public final class UnassignedEvent {
+ ", contractId='"
+ contractId
+ '\''
+ ", packageName="
+ packageName
+ ", templateId="
+ templateId
+ ", source="
@ -154,6 +167,7 @@ public final class UnassignedEvent {
.setUnassignId(this.unassignId)
.setContractId(this.contractId)
.setTemplateId(this.getTemplateId().toProto())
.setPackageName(this.packageName)
.setSource(this.source)
.setTarget(this.target)
.setSubmitter(this.submitter)
@ -168,6 +182,7 @@ public final class UnassignedEvent {
unassignedEvent.getUnassignId(),
unassignedEvent.getContractId(),
Identifier.fromProto(unassignedEvent.getTemplateId()),
unassignedEvent.getPackageName(),
unassignedEvent.getSource(),
unassignedEvent.getTarget(),
unassignedEvent.getSubmitter(),

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: CantonExamples

View File

@ -1 +1 @@
68d8c1f10eca85f31673553157b156875530adf878640f8bd12ecf50bf801868
0c54aa37e1982cbec6a13f7712ad18993e970f3b2f08e68ea0a8a60ebf4f1176

View File

@ -235,6 +235,7 @@ CREATE TABLE lapi_events_consuming_exercise (
-- * shared event information
contract_id VARCHAR(4000) NOT NULL,
template_id INTEGER NOT NULL,
package_name INTEGER NOT NULL,
flat_event_witnesses INTEGER ARRAY NOT NULL DEFAULT ARRAY[], -- stakeholders
tree_event_witnesses INTEGER ARRAY NOT NULL DEFAULT ARRAY[], -- informees
@ -294,6 +295,7 @@ CREATE TABLE lapi_events_non_consuming_exercise (
-- * shared event information
contract_id VARCHAR(4000) NOT NULL,
template_id INTEGER NOT NULL,
package_name INTEGER NOT NULL,
flat_event_witnesses INTEGER ARRAY NOT NULL DEFAULT ARRAY[], -- stakeholders
tree_event_witnesses INTEGER ARRAY NOT NULL DEFAULT ARRAY[], -- informees
@ -350,6 +352,7 @@ CREATE TABLE lapi_events_unassign (
-- * shared event information
contract_id VARCHAR(4000) NOT NULL,
template_id INTEGER NOT NULL,
package_name INTEGER NOT NULL,
flat_event_witnesses INTEGER ARRAY NOT NULL DEFAULT ARRAY[], -- stakeholders
-- * common reassignment

View File

@ -1 +1 @@
f04f9f9a39bb4bf098d2b872a0a42636e2967957d9f616edd20b0e91c1ce91f6
4db04c53914e522326c6570ec12e1b40200855c305989038a9c0272d18bfc1d2

View File

@ -276,6 +276,7 @@ CREATE TABLE lapi_events_consuming_exercise (
-- * shared event information
contract_id text not null,
template_id integer not null,
package_name integer not null,
flat_event_witnesses integer[] default '{}'::integer[] not null, -- stakeholders
tree_event_witnesses integer[] default '{}'::integer[] not null, -- informees
@ -395,6 +396,7 @@ CREATE TABLE lapi_events_non_consuming_exercise (
-- * shared event information
contract_id text not null,
template_id integer not null,
package_name integer not null,
flat_event_witnesses integer[] default '{}'::integer[] not null, -- stakeholders
tree_event_witnesses integer[] default '{}'::integer[] not null, -- informees
@ -446,6 +448,7 @@ CREATE TABLE lapi_events_unassign (
-- * shared event information
contract_id text not null,
template_id integer not null,
package_name integer not null,
flat_event_witnesses integer[] default '{}'::integer[] not null, -- stakeholders
-- * common reassignment

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: ai-analysis

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: bank

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: doctor

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: health-insurance

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: medical-records

View File

@ -24,6 +24,7 @@ import com.digitalasset.canton.domain.sequencing.integrations.state.statemanager
import com.digitalasset.canton.domain.sequencing.sequencer.Sequencer
import com.digitalasset.canton.domain.sequencing.sequencer.block.BlockSequencer
import com.digitalasset.canton.domain.sequencing.sequencer.errors.CreateSubscriptionError
import com.digitalasset.canton.domain.sequencing.sequencer.traffic.SequencerRateLimitManager
import com.digitalasset.canton.error.SequencerBaseError
import com.digitalasset.canton.lifecycle.{
AsyncCloseable,
@ -113,6 +114,7 @@ class BlockSequencerStateManager(
override val maybeLowerTopologyTimestampBound: Option[CantonTimestamp],
override protected val timeouts: ProcessingTimeout,
protected val loggerFactory: NamedLoggerFactory,
rateLimitManager: SequencerRateLimitManager,
)(implicit executionContext: ExecutionContext, closeContext: CloseContext)
extends BlockSequencerStateManagerBase
with NamedLogging {
@ -466,6 +468,18 @@ class BlockSequencerStateManager(
_ <- store.finalizeBlockUpdate(newBlock)
} yield {
updateHeadState(priorHead, newHead)
// Use lastTs here under the following assumptions:
// 1. lastTs represents the timestamp of the last sequenced "send" event of the last block successfully processed
// Specifically, it is the last of the timestamps in the block passed to the rate limiter in the B.U.G for consumed and traffic updates methods.
// After setting safeForPruning to this timestamp, we will not be able to request balances from the balance manager prior to this timestamp.
// 2. This does not impose restrictions on the use of lastSequencerEventTimestamp when calling the rate limiter.
// Meaning it should be possible to use an old lastSequencerEventTimestamp when calling the rate limiter, even if it is older than lastTs here.
// If this changes, we we will need to use lastSequencerEventTimestamp here instead.
// 3. TODO(i15837): Under some HA failover scenarios, this may not be sufficient. Mainly because finalizeBlockUpdate above does not
// use synchronous commits for DB replicas. This has for consequence that theoretically a block could be finalized but not appear
// in the DB replica, while the pruning will be visible in the replica. This would lead the BUG to requesting balances for that block when
// reprocessing it, which would fail because the balances have been pruned. This needs to be considered when implementing HA for the BlockSequencer.
rateLimitManager.safeForPruning(newHead.block.lastTs)
newState
}
}
@ -678,6 +692,7 @@ object BlockSequencerStateManager {
enableInvariantCheck: Boolean,
timeouts: ProcessingTimeout,
loggerFactory: NamedLoggerFactory,
rateLimitManager: SequencerRateLimitManager,
)(implicit
executionContext: ExecutionContext,
traceContext: TraceContext,
@ -698,6 +713,7 @@ object BlockSequencerStateManager {
maybeLowerTopologyTimestampBound = maybeLowerTopologyTimestampBound,
timeouts = timeouts,
loggerFactory = loggerFactory,
rateLimitManager = rateLimitManager,
)
}

View File

@ -53,7 +53,7 @@ import com.digitalasset.canton.topology.transaction.{
}
import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.traffic.TopUpEvent
import com.digitalasset.canton.util.SingleUseCell
import com.digitalasset.canton.util.{FutureUtil, SingleUseCell}
import com.digitalasset.canton.version.ProtocolVersion
import io.grpc.ServerServiceDefinition
import org.apache.pekko.actor.ActorSystem
@ -169,6 +169,12 @@ class SequencerNodeBootstrapX(
loggerFactory,
)
// Start auto pruning of traffic balances
FutureUtil.doNotAwaitUnlessShutdown(
balanceManager.startAutoPruning,
"Auto pruning of traffic balances",
)
// add initialization service
adminServerRegistry.addServiceU(
SequencerInitializationServiceGrpc.bindService(

View File

@ -274,6 +274,7 @@ class SequencerRuntimeForSeparateNode(
override def onClosed(): Unit = {
Lifecycle.close(
Lifecycle.toCloseableOption(sequencer.rateLimitManager),
timeTracker,
syncCrypto,
topologyClient,

View File

@ -487,18 +487,20 @@ class DbSequencerStateManagerStore(
override def prune(requestedTimestamp: CantonTimestamp)(implicit
traceContext: TraceContext
): Future[PruningResult] = for {
numberOfEventsBefore <- numberOfEvents()
numberOfEventsToBeDeleted <- numberOfEventsToBeDeletedByPruneAt(requestedTimestamp)
_ <- pruneEvents(requestedTimestamp)
min <- minCounters
numberOfEventsAfter <- numberOfEvents()
numberOfDeletions = numberOfEventsBefore - numberOfEventsAfter
} yield PruningResult(numberOfDeletions, min)
} yield PruningResult(numberOfEventsToBeDeleted, min)
override protected[state] def numberOfEvents()(implicit
override protected[state] def numberOfEventsToBeDeletedByPruneAt(
requestedTimestamp: CantonTimestamp
)(implicit
traceContext: TraceContext
): Future[Long] =
storage.query(
sql"select count(*) from seq_state_manager_events".as[Long].head,
): Future[Long] = storage
.query(
sql"select count(*) from seq_state_manager_events where ts < $requestedTimestamp"
.as[Long]
.head,
functionFullName,
)

View File

@ -344,10 +344,13 @@ class InMemorySequencerStateManagerStore(
result.get
}
override protected[state] def numberOfEvents()(implicit
override protected[state] def numberOfEventsToBeDeletedByPruneAt(
requestedTimestamp: CantonTimestamp
)(implicit
traceContext: TraceContext
): Future[Long] = Future.successful(state.get().indices.map(_._2.events.size).sum.toLong)
): Future[Long] = Future.successful(
state.get().indices.map(_._2.events.count(_.timestamp < requestedTimestamp)).sum.toLong
)
override def fetchLowerBound()(implicit
traceContext: TraceContext
): Future[Option[CantonTimestamp]] = Future.successful(state.get().pruningLowerBound)

View File

@ -140,9 +140,10 @@ trait SequencerStateManagerStore {
): Future[Option[CantonTimestamp]]
@VisibleForTesting
protected[state] def numberOfEvents()(implicit
traceContext: TraceContext
protected[state] def numberOfEventsToBeDeletedByPruneAt(requestedTimestamp: CantonTimestamp)(
implicit traceContext: TraceContext
): Future[Long]
}
object SequencerStateManagerStore {

View File

@ -148,6 +148,7 @@ abstract class BlockSequencerFactory(
nodeParameters.enableAdditionalConsistencyChecks,
nodeParameters.processingTimeouts,
domainLoggerFactory,
rateLimitManager,
)
}

View File

@ -26,7 +26,7 @@ import scala.concurrent.ExecutionContext
/** Holds the traffic control state and control rate limiting logic of members of a sequencer
*/
trait SequencerRateLimitManager {
trait SequencerRateLimitManager extends AutoCloseable {
/** Create a traffic state for a new member at the given timestamp.
* Its base traffic remainder will be equal to the max burst window configured at that point in time.
@ -102,6 +102,12 @@ trait SequencerRateLimitManager {
/** Optional subscriber to the traffic control processor, only used for the new top up implementation
*/
def balanceUpdateSubscriber: Option[SequencerTrafficControlSubscriber]
/** Marks the provided timestamp as safe for pruning.
* This has for consequence that requesting balances strictly below this timestamp may lead to an UnknownBalance error,
* as the balance will be eligible for pruning.
*/
def safeForPruning(timestamp: CantonTimestamp)(implicit traceContext: TraceContext): Unit
}
sealed trait SequencerRateLimitError

View File

@ -49,4 +49,9 @@ class BalanceUpdateClientImpl(
override lazy val balanceUpdateSubscription: Option[SequencerTrafficControlSubscriber] = Some(
manager.subscription
)
override def safeForPruning(timestamp: CantonTimestamp)(implicit
traceContext: TraceContext
): Unit =
manager.setSafeToPruneBeforeExclusive(timestamp)
}

View File

@ -31,12 +31,14 @@ import com.digitalasset.canton.sequencing.protocol.{
import com.digitalasset.canton.topology.Member
import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.traffic.EventCostCalculator
import com.google.common.annotations.VisibleForTesting
import scala.collection.concurrent.TrieMap
import scala.concurrent.ExecutionContext
class EnterpriseSequencerRateLimitManager(
balanceUpdateClient: BalanceUpdateClient,
@VisibleForTesting
private[canton] val balanceUpdateClient: BalanceUpdateClient,
override protected val loggerFactory: NamedLoggerFactory,
futureSupervisor: FutureSupervisor,
override val timeouts: ProcessingTimeout,
@ -229,6 +231,11 @@ class EnterpriseSequencerRateLimitManager(
}
override def balanceUpdateSubscriber: Option[SequencerTrafficControlSubscriber] =
balanceUpdateClient.balanceUpdateSubscription
override def safeForPruning(timestamp: CantonTimestamp)(implicit
traceContext: TraceContext
): Unit =
balanceUpdateClient.safeForPruning(timestamp)
}
object EnterpriseSequencerRateLimitManager {
@ -264,5 +271,6 @@ object EnterpriseSequencerRateLimitManager {
): FutureUnlessShutdown[Option[TrafficBalance]]
def balanceUpdateSubscription: Option[SequencerTrafficControlSubscriber] = None
def safeForPruning(timestamp: CantonTimestamp)(implicit traceContext: TraceContext): Unit = {}
}
}

View File

@ -445,7 +445,10 @@ class TrafficBalanceManager(
def startAutoPruning(implicit
traceContext: TraceContext
): FutureUnlessShutdown[Unit] = {
lazy val newPromise = mkPromise[Unit]("auto pruning started", futureSupervisor)
// This future will only complete when auto pruning is stopped manually or the node goes down
// so use the noop supervisor to avoid logging continuously that the future is not done
lazy val newPromise =
mkPromise[Unit]("auto pruning started", FutureSupervisor.Noop)
autoPruningPromise.getAndUpdate({
case None => Some(newPromise)
case existing => existing

View File

@ -559,18 +559,20 @@ trait SequencerStateManagerStoreTest
_ <- store.acknowledge(bob, ts(6))
statusBefore <- store.status()
pruningTimestamp = statusBefore.safePruningTimestampFor(now)
eventCountBefore <- store.numberOfEvents()
eventsToBeDeleted <- store.numberOfEventsToBeDeletedByPruneAt(pruningTimestamp)
result <- {
logger.debug(s"Pruning sequencer state manager store from $pruningTimestamp")
store.prune(pruningTimestamp)
}
eventCountAfter <- store.numberOfEvents()
eventsToBeDeletedAfterPruning <- store.numberOfEventsToBeDeletedByPruneAt(
pruningTimestamp
)
statusAfter <- store.status()
lowerBound <- store.fetchLowerBound()
} yield {
result.eventsPruned shouldBe 3L
val eventsRemoved = eventCountBefore - eventCountAfter
eventsRemoved shouldBe 3L
eventsToBeDeleted shouldBe 3L
eventsToBeDeletedAfterPruning shouldBe 0L
statusBefore.lowerBound shouldBe <(statusAfter.lowerBound)
lowerBound.value shouldBe ts(6) // to prevent reads from before this point
result.newMinimumCountersSupported shouldBe Map(

View File

@ -151,6 +151,10 @@ message ArchivedEvent {
// in ``value.proto``).
// Required
repeated string witness_parties = 4;
// The package name of the contract.
// Required
string package_name = 5;
}
// Records that a choice has been exercised on a target contract.
@ -215,4 +219,8 @@ message ExercisedEvent {
// The result of exercising the choice.
// Required
Value exercise_result = 11;
// The package name of the contract.
// Required
string package_name = 12;
}

View File

@ -105,6 +105,10 @@ message UnassignedEvent {
// The parties that are notified of this event.
// Required
repeated string witness_parties = 9;
// The package name of the contract.
// Required
string package_name = 10;
}
// Records that a contract has been assigned, and it can be used on the target domain.

View File

@ -77,7 +77,7 @@ class PersistentUserManagementStore(
resourceVersion = 0,
createdAt = now,
)
val internalId = backend.createUser(user = dbUser)(connection)
val internalId = retryOnceMore(backend.createUser(user = dbUser)(connection))
user.metadata.annotations.foreach { case (key, value) =>
backend.addUserAnnotation(
internalId = internalId,
@ -309,16 +309,23 @@ class PersistentUserManagementStore(
dbMetric: metrics.userManagement.type => DatabaseMetrics
)(
thunk: Connection => Result[T]
)(implicit loggingContext: LoggingContextWithTrace): Future[Result[T]] =
dbDispatcher
.executeSql(dbMetric(metrics.userManagement))(thunk)
)(implicit loggingContext: LoggingContextWithTrace): Future[Result[T]] = {
def execute(): Future[Result[T]] =
dbDispatcher.executeSql(dbMetric(metrics.userManagement))(thunk)
implicit val ec: ExecutionContext = directEc
execute()
.recoverWith { case RetryOnceMoreException(cause) =>
logger.debug("Retrying transaction to handle potential race", cause)
execute()
}
.recover[Result[T]] {
case TooManyUserRightsRuntimeException(userId) => Left(TooManyUserRights(userId))
case ConcurrentUserUpdateDetectedRuntimeException(userId) =>
Left(UserManagementStore.ConcurrentUserUpdate(userId))
case MaxAnnotationsSizeExceededException(userId) =>
Left(UserManagementStore.MaxAnnotationsSizeExceeded(userId))
}(directEc)
}
}
private def toDomainUser(
dbUser: UserManagementStorageBackend.DbUserWithId,
@ -385,6 +392,13 @@ class PersistentUserManagementStore(
val now = timeProvider.getCurrentTime
(now.getEpochSecond * 1000 * 1000) + (now.getNano / 1000)
}
private def retryOnceMore[T](body: => T): T =
try {
body
} catch {
case t: Throwable => throw RetryOnceMoreException(t)
}
}
object PersistentUserManagementStore {
@ -425,3 +439,5 @@ object PersistentUserManagementStore {
loggerFactory = loggerFactory,
)(executionContext, LoggingContextWithTrace(loggerFactory))
}
final case class RetryOnceMoreException(underlying: Throwable) extends RuntimeException

View File

@ -20,6 +20,7 @@ object Reassignment {
*
* @param contractId Contract ID of the underlying contract.
* @param templateId Template ID of the underlying contract.
* @param packageName Package name of the underlying contract's template.
* @param stakeholders Stakeholders of the underlying contract.
* @param assignmentExclusivity Before this time (measured on the target domain), only the submitter
* of the unassignment can initiate the assignment. Defined for
@ -28,6 +29,7 @@ object Reassignment {
final case class Unassign(
contractId: Value.ContractId,
templateId: Ref.Identifier,
packageName: Ref.PackageName,
stakeholders: List[Ref.Party],
assignmentExclusivity: Option[Timestamp],
) extends Reassignment {

View File

@ -335,6 +335,7 @@ private[platform] object InMemoryStateUpdater {
contractId = exercise.targetCoid,
ledgerEffectiveTime = txAccepted.transactionMeta.ledgerEffectiveTime,
templateId = exercise.templateId,
packageName = exercise.packageName,
commandId = txAccepted.completionInfoO.map(_.commandId).getOrElse(""),
workflowId = txAccepted.transactionMeta.workflowId.getOrElse(""),
contractKey =
@ -387,7 +388,7 @@ private[platform] object InMemoryStateUpdater {
offset = offset,
events = events.toVector,
completionDetails = completionDetails,
domainId = Some(txAccepted.domainId.toProtoPrimitive), // TODO(i15280)
domainId = Some(txAccepted.domainId.toProtoPrimitive),
recordTime = txAccepted.recordTime,
)
}

View File

@ -55,6 +55,7 @@ object DbDto {
event_id: String,
contract_id: String,
template_id: String,
package_name: String,
flat_event_witnesses: Set[String],
tree_event_witnesses: Set[String],
create_key_value: Option[Array[Byte]],
@ -109,6 +110,7 @@ object DbDto {
submitter: Option[String],
contract_id: String,
template_id: String,
package_name: String,
flat_event_witnesses: Set[String],
event_sequential_id: Long,
source_domain_id: String,

View File

@ -36,6 +36,8 @@ object DbDtoToStringsForInterning {
dbDto match {
case dbDto: DbDto.EventCreate => Iterator(dbDto.package_name)
case dbDto: DbDto.EventAssign => Iterator(dbDto.package_name)
case dbDto: DbDto.EventExercise => Iterator(dbDto.package_name)
case dbDto: DbDto.EventUnassign => Iterator(dbDto.package_name)
case _ => Iterator.empty
}

View File

@ -403,6 +403,7 @@ object EventStorageBackend {
reassignmentCounter: Long,
contractId: String,
templateId: Identifier,
packageName: PackageName,
witnessParties: Set[String],
assignmentExclusivity: Option[Timestamp],
traceContext: Option[Array[Byte]],

View File

@ -283,6 +283,7 @@ object UpdateToDbDto {
event_id = EventId(u.transactionId, nodeId).toLedgerString,
contract_id = exercise.targetCoid.coid,
template_id = templateId,
package_name = exercise.packageName,
flat_event_witnesses = flatWitnesses,
tree_event_witnesses = informees,
create_key_value = createKeyValue
@ -364,6 +365,7 @@ object UpdateToDbDto {
submitter = u.reassignmentInfo.submitter,
contract_id = unassign.contractId.coid,
template_id = templateId,
package_name = unassign.packageName,
flat_event_witnesses = flatEventWitnesses.toSet,
event_sequential_id = 0L, // this is filled later
source_domain_id = u.reassignmentInfo.sourceDomain.unwrap.toProtoPrimitive,

View File

@ -75,7 +75,7 @@ object EventStorageBackendTemplate {
"event_id",
"contract_id",
"template_id",
"NULL as package_name",
"package_name",
"NULL as create_argument",
"NULL as create_argument_compression",
"NULL as create_signatories",
@ -98,7 +98,7 @@ object EventStorageBackendTemplate {
baseColumnsForFlatTransactionsExercise.mkString(", ")
private type SharedRow =
Offset ~ String ~ Int ~ Long ~ String ~ String ~ Timestamp ~ Int ~ Option[String] ~
Offset ~ String ~ Int ~ Long ~ String ~ String ~ Timestamp ~ Int ~ Int ~ Option[String] ~
Option[String] ~ Array[Int] ~ Option[Array[Int]] ~ Int ~ Option[Array[Byte]] ~ Timestamp
private val sharedRow: RowParser[SharedRow] =
@ -110,6 +110,7 @@ object EventStorageBackendTemplate {
str("contract_id") ~
timestampFromMicros("ledger_effective_time") ~
int("template_id") ~
int("package_name") ~
str("command_id").? ~
str("workflow_id").? ~
array[Int]("event_witnesses") ~
@ -121,7 +122,7 @@ object EventStorageBackendTemplate {
private type CreatedEventRow =
SharedRow ~ Array[Byte] ~ Option[Int] ~ Array[Int] ~ Array[Int] ~
Option[Array[Byte]] ~ Option[Hash] ~ Option[Int] ~ Option[Array[Int]] ~
Option[Array[Byte]] ~ Int
Option[Array[Byte]]
private val createdEventRow: RowParser[CreatedEventRow] =
sharedRow ~
@ -133,8 +134,7 @@ object EventStorageBackendTemplate {
hashFromHexString("create_key_hash").? ~
int("create_key_value_compression").? ~
array[Int]("create_key_maintainers").? ~
byteArray("driver_metadata").? ~
int("package_name")
byteArray("driver_metadata").?
private type ExercisedEventRow =
SharedRow ~ Boolean ~ String ~ Array[Byte] ~ Option[Int] ~ Option[Array[Byte]] ~ Option[Int] ~
@ -163,8 +163,8 @@ object EventStorageBackendTemplate {
): RowParser[EventStorageBackend.Entry[Raw.FlatEvent.Created]] =
createdEventRow map {
case eventOffset ~ transactionId ~ nodeIndex ~ eventSequentialId ~ eventId ~ contractId ~ ledgerEffectiveTime ~
templateId ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime ~ createArgument ~ createArgumentCompression ~
createSignatories ~ createObservers ~ createKeyValue ~ createKeyHash ~ createKeyValueCompression ~ createKeyMaintainers ~ driverMetadata ~ packageName =>
templateId ~ packageName ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime ~ createArgument ~ createArgumentCompression ~
createSignatories ~ createObservers ~ createKeyValue ~ createKeyHash ~ createKeyValueCompression ~ createKeyMaintainers ~ driverMetadata =>
// ArraySeq.unsafeWrapArray is safe here
// since we get the Array from parsing and don't let it escape anywhere.
EventStorageBackend.Entry(
@ -219,7 +219,7 @@ object EventStorageBackendTemplate {
): RowParser[EventStorageBackend.Entry[Raw.FlatEvent.Archived]] =
archivedEventRow map {
case eventOffset ~ transactionId ~ nodeIndex ~ eventSequentialId ~ eventId ~ contractId ~ ledgerEffectiveTime ~
templateId ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime =>
templateId ~ packageName ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime =>
// ArraySeq.unsafeWrapArray is safe here
// since we get the Array from parsing and don't let it escape anywhere.
EventStorageBackend.Entry(
@ -238,6 +238,7 @@ object EventStorageBackendTemplate {
eventId = eventId,
contractId = contractId,
templateId = stringInterning.templateId.externalize(templateId),
packageName = stringInterning.packageName.externalize(packageName),
eventWitnesses = ArraySeq.unsafeWrapArray(
eventWitnesses.view
.filter(allQueryingParties)
@ -266,9 +267,9 @@ object EventStorageBackendTemplate {
): RowParser[EventStorageBackend.Entry[Raw.TreeEvent.Created]] =
createdEventRow map {
case eventOffset ~ transactionId ~ nodeIndex ~ eventSequentialId ~ eventId ~ contractId ~ ledgerEffectiveTime ~
templateId ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime ~
templateId ~ packageName ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime ~
createArgument ~ createArgumentCompression ~ createSignatories ~ createObservers ~
createKeyValue ~ createKeyHash ~ createKeyValueCompression ~ createKeyMaintainers ~ driverMetadata ~ packageName =>
createKeyValue ~ createKeyHash ~ createKeyValueCompression ~ createKeyMaintainers ~ driverMetadata =>
// ArraySeq.unsafeWrapArray is safe here
// since we get the Array from parsing and don't let it escape anywhere.
EventStorageBackend.Entry(
@ -322,7 +323,7 @@ object EventStorageBackendTemplate {
stringInterning: StringInterning,
): RowParser[EventStorageBackend.Entry[Raw.TreeEvent.Exercised]] =
exercisedEventRow map {
case eventOffset ~ transactionId ~ nodeIndex ~ eventSequentialId ~ eventId ~ contractId ~ ledgerEffectiveTime ~ templateId ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime ~ exerciseConsuming ~ qualifiedChoiceName ~ exerciseArgument ~ exerciseArgumentCompression ~ exerciseResult ~ exerciseResultCompression ~ exerciseActors ~ exerciseChildEventIds =>
case eventOffset ~ transactionId ~ nodeIndex ~ eventSequentialId ~ eventId ~ contractId ~ ledgerEffectiveTime ~ templateId ~ packageName ~ commandId ~ workflowId ~ eventWitnesses ~ submitters ~ internedDomainId ~ traceContext ~ recordTime ~ exerciseConsuming ~ qualifiedChoiceName ~ exerciseArgument ~ exerciseArgumentCompression ~ exerciseResult ~ exerciseResultCompression ~ exerciseActors ~ exerciseChildEventIds =>
val Ref.QualifiedChoiceName(interfaceId, choiceName) =
Ref.QualifiedChoiceName.assertFromString(qualifiedChoiceName)
// ArraySeq.unsafeWrapArray is safe here
@ -343,6 +344,7 @@ object EventStorageBackendTemplate {
eventId = eventId,
contractId = contractId,
templateId = stringInterning.templateId.externalize(templateId),
packageName = stringInterning.packageName.externalize(packageName),
interfaceId = interfaceId,
exerciseConsuming = exerciseConsuming,
exerciseChoice = choiceName,
@ -418,7 +420,7 @@ object EventStorageBackendTemplate {
"contract_id",
"ledger_effective_time",
"template_id",
"NULL as package_name",
"package_name",
"workflow_id",
"NULL as create_argument",
"NULL as create_argument_compression",
@ -554,6 +556,7 @@ object EventStorageBackendTemplate {
str("update_id") ~
str("contract_id") ~
int("template_id") ~
int("package_name") ~
array[Int]("flat_event_witnesses") ~
timestampFromMicros("assignment_exclusivity").? ~
byteArray("trace_context").? ~
@ -575,6 +578,7 @@ object EventStorageBackendTemplate {
updateId ~
contractId ~
templateId ~
packageName ~
flatEventWitnesses ~
assignmentExclusivity ~
traceContext ~
@ -591,6 +595,7 @@ object EventStorageBackendTemplate {
updateId = updateId,
contractId = contractId,
templateId = stringInterning.templateId.externalize(templateId),
packageName = stringInterning.packageName.externalize(packageName),
witnessParties = flatEventWitnesses.view
.filter(allQueryingParties)
.map(stringInterning.party.unsafe.externalize)

View File

@ -163,6 +163,9 @@ private[backend] object AppendOnlySchema {
"template_id" -> fieldStrategy.int(stringInterning =>
dbDto => stringInterning.templateId.unsafe.internalize(dbDto.template_id)
),
"package_name" -> fieldStrategy.int(stringInterning =>
dbDto => stringInterning.packageName.unsafe.internalize(dbDto.package_name)
),
"flat_event_witnesses" -> fieldStrategy.intArray(stringInterning =>
_.flat_event_witnesses.map(stringInterning.party.unsafe.internalize)
),
@ -200,6 +203,9 @@ private[backend] object AppendOnlySchema {
"template_id" -> fieldStrategy.int(stringInterning =>
dbDto => stringInterning.templateId.unsafe.internalize(dbDto.template_id)
),
"package_name" -> fieldStrategy.int(stringInterning =>
dbDto => stringInterning.packageName.unsafe.internalize(dbDto.package_name)
),
"flat_event_witnesses" -> fieldStrategy.intArray(stringInterning =>
_.flat_event_witnesses.map(stringInterning.party.unsafe.internalize)
),

View File

@ -268,7 +268,7 @@ private class JdbcLedgerDao(
recordTime = recordTime,
completionInfo = info,
reasonTemplate = reason,
domainId = DomainId.tryFromString("invalid::deadbeef"), // TODO(i15280)
domainId = DomainId.tryFromString("invalid::deadbeef"),
)
)
),
@ -663,7 +663,7 @@ private class JdbcLedgerDao(
blindingInfoO = blindingInfoO,
hostedWitnesses = hostedWitnesses,
contractMetadata = Map.empty,
domainId = DomainId.tryFromString("invalid::deadbeef"), // TODO(i15280)
domainId = DomainId.tryFromString("invalid::deadbeef"),
)
)
),

View File

@ -11,6 +11,7 @@ import com.daml.ledger.api.v2.event.{
}
import com.daml.ledger.api.v2.transaction.TreeEvent as PbTreeEvent
import com.daml.lf.crypto.Hash
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.PackageName
import com.daml.lf.data.Time.Timestamp
import com.digitalasset.canton.ledger.api.util.{LfEngineToApi, TimestampConversion}
@ -205,6 +206,7 @@ object Raw {
eventId: String,
contractId: String,
templateId: Identifier,
packageName: Ref.PackageName,
eventWitnesses: ArraySeq[String],
): Raw.FlatEvent.Archived =
new Raw.FlatEvent.Archived(
@ -212,6 +214,7 @@ object Raw {
eventId = eventId,
contractId = contractId,
templateId = Some(LfEngineToApi.toApiIdentifier(templateId)),
packageName = packageName,
witnessParties = eventWitnesses,
)
)
@ -314,6 +317,7 @@ object Raw {
eventId: String,
contractId: String,
templateId: Identifier,
packageName: PackageName,
interfaceId: Option[Identifier],
exerciseConsuming: Boolean,
exerciseChoice: String,
@ -330,6 +334,7 @@ object Raw {
eventId = eventId,
contractId = contractId,
templateId = Some(LfEngineToApi.toApiIdentifier(templateId)),
packageName = packageName,
interfaceId = interfaceId.map(LfEngineToApi.toApiIdentifier),
choice = exerciseChoice,
choiceArgument = null,

View File

@ -240,6 +240,7 @@ private[events] object TransactionLogUpdatesConversions {
eventId = exercisedEvent.eventId.toLedgerString,
contractId = exercisedEvent.contractId.coid,
templateId = Some(LfEngineToApi.toApiIdentifier(exercisedEvent.templateId)),
packageName = exercisedEvent.packageName,
witnessParties =
requestingParties.iterator.filter(exercisedEvent.flatEventWitnesses).toSeq,
)
@ -456,6 +457,7 @@ private[events] object TransactionLogUpdatesConversions {
eventId = exercisedEvent.eventId.toLedgerString,
contractId = exercisedEvent.contractId.coid,
templateId = Some(LfEngineToApi.toApiIdentifier(exercisedEvent.templateId)),
packageName = exercisedEvent.packageName,
interfaceId = exercisedEvent.interfaceId.map(LfEngineToApi.toApiIdentifier),
choice = exercisedEvent.choice,
choiceArgument = Some(choiceArgument),
@ -595,6 +597,7 @@ private[events] object TransactionLogUpdatesConversions {
reassignmentCounter = info.reassignmentCounter,
contractId = unassign.contractId.coid,
templateId = Some(LfEngineToApi.toApiIdentifier(unassign.templateId)),
packageName = unassign.packageName,
assignmentExclusivity =
unassign.assignmentExclusivity.map(TimestampConversion.fromLf),
witnessParties = reassignmentAccepted.reassignmentInfo.hostedStakeholders

View File

@ -322,6 +322,7 @@ private[dao] object TransactionsReader {
unassignId = rawUnassignEvent.unassignId,
contractId = rawUnassignEvent.contractId,
templateId = Some(LfEngineToApi.toApiIdentifier(rawUnassignEvent.templateId)),
packageName = rawUnassignEvent.packageName,
source = rawUnassignEvent.sourceDomainId,
target = rawUnassignEvent.targetDomainId,
submitter = rawUnassignEvent.submitter.getOrElse(""),

View File

@ -137,6 +137,7 @@ object TransactionLogUpdate {
contractId: ContractId,
ledgerEffectiveTime: Timestamp,
templateId: Identifier,
packageName: PackageName,
interfaceId: Option[Identifier],
commandId: String,
workflowId: String,

View File

@ -22,6 +22,7 @@ import com.digitalasset.canton.ledger.localstore.api.{
import com.digitalasset.canton.logging.LoggingContextWithTrace
import org.scalatest.freespec.AsyncFreeSpec
import scala.concurrent.Future
import scala.language.implicitConversions
/** Common tests for implementations of [[UserManagementStore]]
@ -150,6 +151,19 @@ trait UserStoreTests extends UserStoreSpecBase { self: AsyncFreeSpec =>
}
}
"disallow re-creating an existing user concurrently" in {
testIt { tested =>
val user = newUser("user1")
for {
res <- Future.sequence(
Seq(tested.createUser(user, Set.empty), tested.createUser(user, Set.empty))
)
} yield {
res should contain(Left(UserExists(user.id)))
}
}
}
"deny permission re-creating an existing user within another IDP" in {
testIt { tested =>
val user = newUser("user1")

View File

@ -305,6 +305,7 @@ object InMemoryStateUpdaterSpec {
Reassignment.Unassign(
contractId = someCreateNode.coid,
templateId = templateId2,
packageName = packageName,
stakeholders = List(party2),
assignmentExclusivity = Some(Timestamp.assertFromLong(123456L)),
)
@ -614,6 +615,7 @@ object InMemoryStateUpdaterSpec {
reassignment = Reassignment.Unassign(
contractId = someCreateNode.coid,
templateId = templateId2,
packageName = packageName,
stakeholders = List(party2),
assignmentExclusivity = Some(Timestamp.assertFromLong(123456L)),
),

View File

@ -108,6 +108,7 @@ class ParallelIndexerSubscriptionSpec extends AnyFlatSpec with Matchers with Nam
event_id = "",
contract_id = "1",
template_id = "",
package_name = "",
flat_event_witnesses = Set.empty,
tree_event_witnesses = Set.empty,
create_key_value = None,
@ -162,6 +163,7 @@ class ParallelIndexerSubscriptionSpec extends AnyFlatSpec with Matchers with Nam
submitter = None,
contract_id = "",
template_id = "",
package_name = "",
flat_event_witnesses = Set.empty,
event_sequential_id = 0,
source_domain_id = "",

View File

@ -71,7 +71,9 @@ class DbDtoToStringsForInterningSpec extends AnyFlatSpec with Matchers {
).sorted
iterators.packageNames.toList.sorted shouldBe List(
"25.1",
"50.1",
"87.1",
"94.1",
).sorted
}
@ -135,6 +137,7 @@ class DbDtoToStringsForInterningSpec extends AnyFlatSpec with Matchers {
event_id = "48",
contract_id = "49",
template_id = "50",
package_name = "50.1",
flat_event_witnesses = Set("51", "52", "53"),
tree_event_witnesses = Set("54", "55", "56"),
exercise_argument = Array.empty,
@ -229,6 +232,7 @@ class DbDtoToStringsForInterningSpec extends AnyFlatSpec with Matchers {
submitter = Option("s2"),
contract_id = "",
template_id = "94",
package_name = "94.1",
flat_event_witnesses = Set("95", "96"),
event_sequential_id = 0,
source_domain_id = "domain7",

View File

@ -208,6 +208,7 @@ private[store] object StorageBackendTestValues {
event_id = EventId(transactionId, NodeId(0)).toLedgerString,
contract_id = contractId.coid,
template_id = someTemplateId.toString,
package_name = somePackageName,
flat_event_witnesses = if (consuming) Set(signatory) else Set.empty,
tree_event_witnesses = Set(signatory, actor),
create_key_value = None,
@ -289,6 +290,7 @@ private[store] object StorageBackendTestValues {
submitter = Option(someParty),
contract_id = contractId.coid,
template_id = someTemplateId.toString,
package_name = somePackageName,
flat_event_witnesses = Set(signatory, observer),
event_sequential_id = eventSequentialId,
source_domain_id = sourceDomainId,

View File

@ -173,13 +173,13 @@ private[backend] trait StorageBackendTestsPruning
def assertAllDataPresent(): Assertion = assertIndexDbDataSql(
consuming = Vector(EventConsuming(6), EventConsuming(9)),
consumingFilterStakeholder =
Vector(FilterConsumingStakeholder(6, 3), FilterConsumingStakeholder(9, 3)),
Vector(FilterConsumingStakeholder(6, 4), FilterConsumingStakeholder(9, 4)),
consumingFilterNonStakeholder =
Vector(FilterConsumingNonStakeholder(6, 1), FilterConsumingNonStakeholder(9, 1)),
nonConsuming = Vector(EventNonConsuming(5), EventNonConsuming(8)),
nonConsumingFilter = Vector(FilterNonConsuming(5, 3), FilterNonConsuming(8, 3)),
nonConsumingFilter = Vector(FilterNonConsuming(5, 4), FilterNonConsuming(8, 4)),
unassign = Vector(EventUnassign(7), EventUnassign(10)),
unassignFilter = Vector(FilterUnassign(7, 3), FilterUnassign(10, 3)),
unassignFilter = Vector(FilterUnassign(7, 4), FilterUnassign(10, 4)),
)
assertAllDataPresent()
@ -190,18 +190,18 @@ private[backend] trait StorageBackendTestsPruning
pruneEventsSql(offset(5), pruneAllDivulgedContracts = true)
assertIndexDbDataSql(
consuming = Vector(EventConsuming(9)),
consumingFilterStakeholder = Vector(FilterConsumingStakeholder(9, 3)),
consumingFilterStakeholder = Vector(FilterConsumingStakeholder(9, 4)),
consumingFilterNonStakeholder = Vector(FilterConsumingNonStakeholder(9, 1)),
nonConsuming = Vector(EventNonConsuming(8)),
nonConsumingFilter = Vector(FilterNonConsuming(8, 3)),
nonConsumingFilter = Vector(FilterNonConsuming(8, 4)),
unassign = Vector(EventUnassign(10)),
unassignFilter = Vector(FilterUnassign(10, 3)),
unassignFilter = Vector(FilterUnassign(10, 4)),
)
// Prune at the ledger end, but setting the unassign incomplete
pruneEventsSql(endOffset, pruneAllDivulgedContracts = true, Vector(offset(8)))
assertIndexDbDataSql(
unassign = Vector(EventUnassign(10)),
unassignFilter = Vector(FilterUnassign(10, 3)),
unassignFilter = Vector(FilterUnassign(10, 4)),
)
// Prune at the ledger end
pruneEventsSql(endOffset, pruneAllDivulgedContracts = true)
@ -580,10 +580,10 @@ private[backend] trait StorageBackendTestsPruning
def assertAllDataPresent(txMeta: Vector[TxMeta]): Assertion = assertIndexDbDataSql(
assign = Vector(EventAssign(1)),
assignFilter = Vector(FilterAssign(1, 3)),
assignFilter = Vector(FilterAssign(1, 4)),
consuming = Vector(EventConsuming(2)),
consumingFilterStakeholder = Vector(
FilterConsumingStakeholder(2, 3),
FilterConsumingStakeholder(2, 4),
FilterConsumingStakeholder(2, 7),
),
txMeta = txMeta,
@ -646,11 +646,9 @@ private[backend] trait StorageBackendTestsPruning
def assertAllDataPresent(txMeta: Vector[TxMeta]): Assertion = assertIndexDbDataSql(
assign = Vector(EventAssign(1)),
assignFilter = Vector(FilterAssign(1, 3)),
assignFilter = Vector(FilterAssign(1, 4)),
unassign = Vector(EventUnassign(2)),
unassignFilter = Vector(
FilterUnassign(2, 3)
),
unassignFilter = Vector(FilterUnassign(2, 4)),
txMeta = txMeta,
)
@ -789,14 +787,14 @@ private[backend] trait StorageBackendTestsPruning
def assertAllDataPresent(txMeta: Seq[TxMeta]): Assertion = assertIndexDbDataSql(
assign = Vector(EventAssign(4)),
assignFilter = Vector(FilterAssign(4, 3)),
assignFilter = Vector(FilterAssign(4, 4)),
consuming =
Vector(EventConsuming(1), EventConsuming(5), EventConsuming(6), EventConsuming(9)),
consumingFilterStakeholder = Vector(
FilterConsumingStakeholder(1, 3),
FilterConsumingStakeholder(5, 3),
FilterConsumingStakeholder(6, 3),
FilterConsumingStakeholder(9, 3),
FilterConsumingStakeholder(1, 4),
FilterConsumingStakeholder(5, 4),
FilterConsumingStakeholder(6, 4),
FilterConsumingStakeholder(9, 4),
),
unassign = Vector(
EventUnassign(2),
@ -806,11 +804,11 @@ private[backend] trait StorageBackendTestsPruning
EventUnassign(10),
),
unassignFilter = Vector(
FilterUnassign(2, 3),
FilterUnassign(3, 3),
FilterUnassign(7, 3),
FilterUnassign(8, 3),
FilterUnassign(10, 3),
FilterUnassign(2, 4),
FilterUnassign(3, 4),
FilterUnassign(7, 4),
FilterUnassign(8, 4),
FilterUnassign(10, 4),
),
txMeta = txMeta,
)
@ -848,12 +846,12 @@ private[backend] trait StorageBackendTestsPruning
pruneEventsSql(offset(5), pruneAllDivulgedContracts = true)
assertIndexDbDataSql(
assign = Vector(EventAssign(4)),
assignFilter = Vector(FilterAssign(4, 3)),
assignFilter = Vector(FilterAssign(4, 4)),
consuming = Vector(EventConsuming(5), EventConsuming(6), EventConsuming(9)),
consumingFilterStakeholder = Vector(
FilterConsumingStakeholder(5, 3),
FilterConsumingStakeholder(6, 3),
FilterConsumingStakeholder(9, 3),
FilterConsumingStakeholder(5, 4),
FilterConsumingStakeholder(6, 4),
FilterConsumingStakeholder(9, 4),
),
unassign = Vector(
EventUnassign(7),
@ -861,9 +859,9 @@ private[backend] trait StorageBackendTestsPruning
EventUnassign(10),
),
unassignFilter = Vector(
FilterUnassign(7, 3),
FilterUnassign(8, 3),
FilterUnassign(10, 3),
FilterUnassign(7, 4),
FilterUnassign(8, 4),
FilterUnassign(10, 4),
),
txMeta = Vector(
TxMeta("00000006"),
@ -878,16 +876,16 @@ private[backend] trait StorageBackendTestsPruning
pruneEventsSql(offset(9), pruneAllDivulgedContracts = true)
assertIndexDbDataSql(
assign = Vector(EventAssign(4)),
assignFilter = Vector(FilterAssign(4, 3)),
assignFilter = Vector(FilterAssign(4, 4)),
consuming = Vector(EventConsuming(9)),
consumingFilterStakeholder = Vector(
FilterConsumingStakeholder(9, 3)
FilterConsumingStakeholder(9, 4)
),
unassign = Vector(
EventUnassign(10)
),
unassignFilter = Vector(
FilterUnassign(10, 3)
FilterUnassign(10, 4)
),
txMeta = Vector(
TxMeta("00000010"),
@ -903,16 +901,16 @@ private[backend] trait StorageBackendTestsPruning
)
assertIndexDbDataSql(
assign = Vector(EventAssign(4)),
assignFilter = Vector(FilterAssign(4, 3)),
assignFilter = Vector(FilterAssign(4, 4)),
consuming = Vector(EventConsuming(9)),
consumingFilterStakeholder = Vector(
FilterConsumingStakeholder(9, 3)
FilterConsumingStakeholder(9, 4)
),
unassign = Vector(
EventUnassign(10)
),
unassignFilter = Vector(
FilterUnassign(10, 3)
FilterUnassign(10, 4)
),
txMeta = Vector.empty,
)
@ -925,12 +923,12 @@ private[backend] trait StorageBackendTestsPruning
)
assertIndexDbDataSql(
assign = Vector(EventAssign(4)),
assignFilter = Vector(FilterAssign(4, 3)),
assignFilter = Vector(FilterAssign(4, 4)),
unassign = Vector(
EventUnassign(10)
),
unassignFilter = Vector(
FilterUnassign(10, 3)
FilterUnassign(10, 4)
),
txMeta = Vector.empty,
)

View File

@ -312,6 +312,7 @@ private[backend] trait StorageBackendTestsReassignmentEvents
reassignmentCounter = 1000L,
contractId = hashCid("#1").coid,
templateId = someTemplateId,
packageName = somePackageName,
updateId = offset(1).toHexString,
witnessParties = Set("signatory"),
assignmentExclusivity = Some(Time.Timestamp.assertFromLong(11111)),
@ -329,6 +330,7 @@ private[backend] trait StorageBackendTestsReassignmentEvents
reassignmentCounter = 1000L,
contractId = hashCid("#2").coid,
templateId = someTemplateId,
packageName = somePackageName,
updateId = offset(2).toHexString,
witnessParties = Set("signatory"),
assignmentExclusivity = Some(Time.Timestamp.assertFromLong(11111)),

View File

@ -406,6 +406,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeId).toLedgerString,
contract_id = exerciseNode.targetCoid.coid,
template_id = exerciseNode.templateId.toString,
package_name = exerciseNode.packageName,
flat_event_witnesses = Set("signatory", "observer"), // stakeholders
tree_event_witnesses = Set("signatory", "observer"), // informees
create_key_value = None,
@ -511,6 +512,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeId).toLedgerString,
contract_id = exerciseNode.targetCoid.coid,
template_id = exerciseNode.templateId.toString,
package_name = exerciseNode.packageName,
flat_event_witnesses = Set.empty, // stakeholders
tree_event_witnesses = Set("signatory"), // informees
create_key_value = None,
@ -636,6 +638,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeAId).toLedgerString,
contract_id = exerciseNodeA.targetCoid.coid,
template_id = exerciseNodeA.templateId.toString,
package_name = exerciseNodeA.packageName,
flat_event_witnesses = Set.empty, // stakeholders
tree_event_witnesses = Set("signatory"), // informees
create_key_value = None,
@ -672,6 +675,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeBId).toLedgerString,
contract_id = exerciseNodeB.targetCoid.coid,
template_id = exerciseNodeB.templateId.toString,
package_name = exerciseNodeB.packageName,
flat_event_witnesses = Set.empty, // stakeholders
tree_event_witnesses = Set("signatory"), // informees
create_key_value = None,
@ -705,6 +709,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeCId).toLedgerString,
contract_id = exerciseNodeC.targetCoid.coid,
template_id = exerciseNodeC.templateId.toString,
package_name = exerciseNodeC.packageName,
flat_event_witnesses = Set.empty, // stakeholders
tree_event_witnesses = Set("signatory"), // informees
create_key_value = None,
@ -881,6 +886,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeId).toLedgerString,
contract_id = exerciseNode.targetCoid.coid,
template_id = exerciseNode.templateId.toString,
package_name = exerciseNode.packageName,
flat_event_witnesses = Set("signatory", "observer"),
tree_event_witnesses = Set("signatory", "observer", "divulgee"),
create_key_value = None,
@ -1025,6 +1031,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
event_id = EventId(transactionId, exerciseNodeId).toLedgerString,
contract_id = exerciseNode.targetCoid.coid,
template_id = exerciseNode.templateId.toString,
package_name = exerciseNode.packageName,
flat_event_witnesses = Set("signatory", "observer"),
tree_event_witnesses = Set("signatory", "observer", "divulgee"),
create_key_value = None,
@ -1572,6 +1579,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
reassignment = Reassignment.Unassign(
contractId = contractId,
templateId = createNode.templateId,
packageName = createNode.packageName,
stakeholders =
List("signatory12", "observer23", "asdasdasd").map(Ref.Party.assertFromString),
assignmentExclusivity = Some(Time.Timestamp.assertFromLong(123456)),
@ -1588,6 +1596,7 @@ class UpdateToDbDtoSpec extends AnyWordSpec with Matchers {
submitter = someParty,
contract_id = createNode.coid.coid,
template_id = createNode.templateId.toString,
package_name = createNode.packageName,
flat_event_witnesses = Set("signatory12", "observer23", "asdasdasd"),
event_sequential_id = 0,
source_domain_id = "x::domain1",

View File

@ -251,6 +251,7 @@ object SequentialWriteDaoSpec {
event_id = "",
contract_id = "1",
template_id = "",
package_name = "2",
flat_event_witnesses = Set.empty,
tree_event_witnesses = Set.empty,
create_key_value = None,

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: carbonv1-tests
source: .
version: 3.0.0

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: carbonv2-tests
data-dependencies:
- ../../../../scala-2.13/resource_managed/main/carbonv1-tests-3.0.0.dar

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: experimental-tests
source: .
version: 3.0.0

View File

@ -22,6 +22,10 @@ template Dummy
controller operator
do assert False
nonconsuming choice DummyNonConsuming: ()
controller operator
do return ()
nonconsuming choice Clone: ContractId Dummy
controller operator
do create Dummy with operator

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: model-tests
source: .
version: 3.0.0

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: package-management-tests
source: .
version: 3.0.0

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: semantic-tests
source: .
version: 3.0.0

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: upgrade-tests
source: .
version: 1.0.0

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: upgrade-tests
source: .
version: 2.0.0

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
name: upgrade-tests
source: .
version: 3.0.0

View File

@ -0,0 +1 @@
sbt.version=1.9.7

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: JsonEncodingTest

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.dev
name: JsonEncodingTestDev

View File

@ -1,4 +1,4 @@
sdk-version: 3.0.0-snapshot.20240306.12855.0.v7bee7ef3
sdk-version: 3.0.0-snapshot.20240307.12859.0.v66d83e43
build-options:
- --target=2.1
name: AdminWorkflows

View File

@ -175,6 +175,7 @@ message TransferredOut {
bool is_transferring_participant = 12;
repeated string hosted_stakeholders = 13;
int64 transfer_counter = 14;
string package_name = 15;
}
message TransferredIn {

View File

@ -345,6 +345,7 @@ private final class ChangeAssignation(
submitter = None,
contractId = contract.payload.contract.contractId,
templateId = Option(contract.payload.contract.contractInstance.unversioned.template),
packageName = contract.payload.contract.contractInstance.unversioned.packageName,
contractStakeholders = contract.payload.contract.metadata.stakeholders,
transferId = transferId,
targetDomain = targetDomainId,

View File

@ -132,8 +132,8 @@ class SyncDomainMetrics(
@MetricDoc.Tag(
summary = "Size of conflict detection task queue",
description = """The task scheduler will schedule tasks to run at a given timestamp. This metric
|exposes the number of tasks that are waiting in the task queue for the right time to pass.
description = """This metric measures the size of the queue for conflict detection between
|concurrent transactions.
|A huge number does not necessarily indicate a bottleneck;
|it could also mean that a huge number of tasks have not yet arrived at their execution time.""",
qualification = Debug,
@ -147,11 +147,10 @@ class SyncDomainMetrics(
object transactionProcessing extends TransactionProcessingMetrics(prefix, factory)
@MetricDoc.Tag(
summary = "Size of conflict detection task queue",
description = """The task scheduler will schedule tasks to run at a given timestamp. This metric
|exposes the number of tasks that are waiting in the task queue for the right time to pass.
|A huge number does not necessarily indicate a bottleneck;
|it could also mean that a huge number of tasks have not yet arrived at their execution time.""",
summary = "Number of requests being validated on the domain.",
description = """Number of requests that are currently being validated on the domain.
|This also covers requests submitted by other participants.
|""",
qualification = Debug,
)
val numDirtyRequests: Counter = factory.counter(prefix :+ "dirty-requests")

View File

@ -4,7 +4,6 @@
package com.digitalasset.canton.participant.protocol
import cats.data.OptionT
import com.daml.scalautil.Statement.discard
import com.digitalasset.canton.concurrent.{FutureSupervisor, SupervisedPromise}
import com.digitalasset.canton.data.{
CantonTimestamp,
@ -156,16 +155,12 @@ class RequestJournal(
}
private def incrementNumDirtyRequests(): Unit = {
discard {
numDirtyRequests.incrementAndGet()
}
numDirtyRequests.incrementAndGet().discard
metrics.numDirtyRequests.inc()
}
private def decrementNumDirtyRequests(): Unit = {
discard {
numDirtyRequests.decrementAndGet()
}
numDirtyRequests.decrementAndGet().discard
metrics.numDirtyRequests.dec()
}

View File

@ -48,6 +48,7 @@ import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.canton.util.EitherTUtil.{condUnitET, ifThenET}
import com.digitalasset.canton.version.Transfer.{SourceProtocolVersion, TargetProtocolVersion}
import com.digitalasset.canton.{
LfPackageName,
LfPartyId,
RequestCounter,
SequencerCounter,
@ -454,6 +455,7 @@ class TransferOutProcessingSteps(
WithContractHash.fromContract(contract, fullTree.contractId),
fullTree.transferCounter,
contract.rawContractInstance.contractInstance.unversioned.template,
contract.rawContractInstance.contractInstance.unversioned.packageName,
transferringParticipant,
fullTree.submitterMetadata,
transferId,
@ -544,6 +546,7 @@ class TransferOutProcessingSteps(
WithContractHash(contractId, contractHash),
transferCounter,
templateId,
packageName,
transferringParticipant,
submitterMetadata,
transferId,
@ -600,6 +603,7 @@ class TransferOutProcessingSteps(
transferOutEvent <- createTransferredOut(
contractId,
templateId,
packageName,
stakeholders,
submitterMetadata,
transferId,
@ -631,6 +635,7 @@ class TransferOutProcessingSteps(
private def createTransferredOut(
contractId: LfContractId,
templateId: LfTemplateId,
packageName: LfPackageName,
contractStakeholders: Set[LfPartyId],
submitterMetadata: TransferSubmitterMetadata,
transferId: TransferId,
@ -663,6 +668,7 @@ class TransferOutProcessingSteps(
submitter = Option(submitterMetadata.submitter),
contractId = contractId,
templateId = Some(templateId),
packageName = packageName,
contractStakeholders = contractStakeholders,
transferId = transferId,
targetDomain = targetDomain,
@ -768,6 +774,7 @@ object TransferOutProcessingSteps {
contractIdAndHash: WithContractHash[LfContractId],
transferCounter: TransferCounter,
templateId: LfTemplateId,
packageName: LfPackageName,
transferringParticipant: Boolean,
submitterMetadata: TransferSubmitterMetadata,
transferId: TransferId,

View File

@ -1010,6 +1010,7 @@ private[store] final case class SerializableTransferredOut(
submitter,
contractId,
templateId,
packageName,
contractStakeholders,
transferId,
target,
@ -1026,6 +1027,7 @@ private[store] final case class SerializableTransferredOut(
recordTime = SerializableLfTimestamp(transferId.transferOutTimestamp.underlying).toProtoV30,
contractId = contractId.toProtoPrimitive,
templateId = templateId.map(_.toString).getOrElse(""),
packageName = packageName,
contractStakeholders = contractStakeholders.toSeq,
sourceDomain = transferId.sourceDomain.toProtoPrimitive,
targetDomain = target.toProtoPrimitive,
@ -1057,6 +1059,7 @@ private[store] object SerializableTransferredOut {
isTransferringParticipant,
hostedStakeholdersP,
transferCounterP,
packageNameP,
) = transferOutP
for {
@ -1074,6 +1077,7 @@ private[store] object SerializableTransferredOut {
)
workflowId <- ProtoConverter.parseLFWorkflowIdO(workflowIdP)
templateId <- ProtoConverter.parseTemplateIdO(templateIdP)
packageName <- ProtoConverter.parseLfPackageName(packageNameP)
hostedStakeholders <- hostedStakeholdersP.traverse(ProtoConverter.parseLfPartyId)
} yield LedgerSyncEvent.TransferredOut(
updateId = updateId,
@ -1081,6 +1085,7 @@ private[store] object SerializableTransferredOut {
submitter = submitter,
contractId = contractId,
templateId = templateId,
packageName = packageName,
contractStakeholders = contractStakeholders.toSet,
transferId = TransferId(SourceDomainId(rawSourceDomainId), CantonTimestamp(recordTime)),
targetDomain = TargetDomainId(rawTargetDomainId),

View File

@ -39,6 +39,7 @@ import com.digitalasset.canton.{
LedgerParticipantId,
LedgerSubmissionId,
LedgerTransactionId,
LfPackageName,
LfPartyId,
LfTimestamp,
LfWorkflowId,
@ -435,6 +436,7 @@ object LedgerSyncEvent {
submitter: Option[LfPartyId],
contractId: LfContractId,
templateId: Option[LfTemplateId],
packageName: LfPackageName,
contractStakeholders: Set[LfPartyId],
transferId: TransferId,
targetDomain: TargetDomainId,
@ -461,6 +463,7 @@ object LedgerSyncEvent {
param("transferId", _.transferId),
param("contractId", _.contractId),
paramIfDefined("templateId", _.templateId),
param("packageName", _.packageName),
param("target", _.targetDomain),
paramIfDefined("transferInExclusivity", _.transferInExclusivity),
paramIfDefined("workflowId", _.workflowId),
@ -488,6 +491,7 @@ object LedgerSyncEvent {
s"templateId should not be empty in transfer-id: $transferId"
)
),
packageName = packageName,
stakeholders = contractStakeholders.toList,
assignmentExclusivity = transferInExclusivity,
),

View File

@ -31,7 +31,6 @@ class TrafficStateController(
implicit tc: TraceContext
): Unit = {
metrics.trafficControl.topologyTransaction.updateValue(newBalance.value)
metrics.trafficControl.extraTrafficAvailable.updateValue(newBalance.value)
val newState = currentTrafficState.updateAndGet {
case Some(old) if old.timestamp <= timestamp =>
old.state
@ -45,6 +44,9 @@ class TrafficStateController(
}
case other => other
}
newState
.map(_.state.extraTrafficRemainder.value)
.foreach(metrics.trafficControl.extraTrafficAvailable.updateValue)
logger.debug(s"Updating traffic state after balance update to $newState")
}

View File

@ -60,6 +60,7 @@ import com.digitalasset.canton.{
LedgerApplicationId,
LedgerCommandId,
LfPackageId,
LfPackageName,
LfPartyId,
RequestCounter,
SequencerCounter,
@ -106,6 +107,8 @@ final class TransferOutProcessingStepsTest
private val templateId =
LfTemplateId.assertFromString("transferoutprocessingstepstestpackage:template:id")
private val packageName =
LfPackageName.assertFromString("transferoutprocessingstepstestpackagename")
private val initialTransferCounter: TransferCounterO =
Some(TransferCounter.Genesis)
@ -831,6 +834,7 @@ final class TransferOutProcessingStepsTest
WithContractHash(contractId, contractHash),
TransferCounter.Genesis,
templateId = templateId,
packageName = packageName,
transferringParticipant = false,
submitterMetadata = submitterMetadata(submitter),
transferId,

View File

@ -258,18 +258,28 @@ object TransactionGenerator {
val archivedEventGen: Gen[(Archived, data.ArchivedEvent)] = for {
eventId <- nonEmptyId
pkgName <- nonEmptyId
contractId <- nonEmptyId
(scalaTemplateId, javaTemplateId) <- identifierGen
parties <- Gen.listOf(nonEmptyId)
} yield (
Archived(ArchivedEvent(eventId, contractId, Some(scalaTemplateId), parties)),
new data.ArchivedEvent(parties.asJava, eventId, javaTemplateId, contractId),
Archived(
ArchivedEvent(
eventId = eventId,
contractId = contractId,
templateId = Some(scalaTemplateId),
witnessParties = parties,
packageName = pkgName,
)
),
new data.ArchivedEvent(parties.asJava, eventId, javaTemplateId, pkgName, contractId),
)
val exercisedEventGen: Gen[(Exercised, data.ExercisedEvent)] = for {
eventId <- nonEmptyId
contractId <- nonEmptyId
(scalaTemplateId, javaTemplateId) <- identifierGen
pkgName <- nonEmptyId
mbInterfaceId <- Gen.option(identifierGen)
scalaInterfaceId = mbInterfaceId.map(_._1)
javaInterfaceId = mbInterfaceId.map(_._2)
@ -283,23 +293,25 @@ object TransactionGenerator {
} yield (
Exercised(
ExercisedEvent(
eventId,
contractId,
Some(scalaTemplateId),
scalaInterfaceId,
choice,
Some(scalaChoiceArgument),
actingParties,
consuming,
witnessParties,
Nil,
Some(scalaExerciseResult),
eventId = eventId,
contractId = contractId,
templateId = Some(scalaTemplateId),
interfaceId = scalaInterfaceId,
choice = choice,
choiceArgument = Some(scalaChoiceArgument),
actingParties = actingParties,
consuming = consuming,
witnessParties = witnessParties,
childEventIds = Nil,
exerciseResult = Some(scalaExerciseResult),
packageName = pkgName,
)
),
new data.ExercisedEvent(
witnessParties.asJava,
eventId,
javaTemplateId,
pkgName,
javaInterfaceId.toJava,
contractId,
choice,