mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-13 22:34:31 +03:00
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:
parent
1e671b6356
commit
405f1ae6f7
@ -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,22 +1615,18 @@ 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)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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)),
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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] {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ public final class CreatedEvent implements Event, TreeEvent {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ public interface Event {
|
||||
@NonNull
|
||||
Identifier getTemplateId();
|
||||
|
||||
@NonNull
|
||||
String getPackageName();
|
||||
|
||||
@NonNull
|
||||
String getContractId();
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -25,6 +25,9 @@ public interface TreeEvent {
|
||||
@NonNull
|
||||
Identifier getTemplateId();
|
||||
|
||||
@NonNull
|
||||
String getPackageName();
|
||||
|
||||
@NonNull
|
||||
String getContractId();
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
68d8c1f10eca85f31673553157b156875530adf878640f8bd12ecf50bf801868
|
||||
0c54aa37e1982cbec6a13f7712ad18993e970f3b2f08e68ea0a8a60ebf4f1176
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
f04f9f9a39bb4bf098d2b872a0a42636e2967957d9f616edd20b0e91c1ce91f6
|
||||
4db04c53914e522326c6570ec12e1b40200855c305989038a9c0272d18bfc1d2
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -274,6 +274,7 @@ class SequencerRuntimeForSeparateNode(
|
||||
|
||||
override def onClosed(): Unit = {
|
||||
Lifecycle.close(
|
||||
Lifecycle.toCloseableOption(sequencer.rateLimitManager),
|
||||
timeTracker,
|
||||
syncCrypto,
|
||||
topologyClient,
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -148,6 +148,7 @@ abstract class BlockSequencerFactory(
|
||||
nodeParameters.enableAdditionalConsistencyChecks,
|
||||
nodeParameters.processingTimeouts,
|
||||
domainLoggerFactory,
|
||||
rateLimitManager,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 = {}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -403,6 +403,7 @@ object EventStorageBackend {
|
||||
reassignmentCounter: Long,
|
||||
contractId: String,
|
||||
templateId: Identifier,
|
||||
packageName: PackageName,
|
||||
witnessParties: Set[String],
|
||||
assignmentExclusivity: Option[Timestamp],
|
||||
traceContext: Option[Array[Byte]],
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
),
|
||||
|
@ -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"),
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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(""),
|
||||
|
@ -137,6 +137,7 @@ object TransactionLogUpdate {
|
||||
contractId: ContractId,
|
||||
ledgerEffectiveTime: Timestamp,
|
||||
templateId: Identifier,
|
||||
packageName: PackageName,
|
||||
interfaceId: Option[Identifier],
|
||||
commandId: String,
|
||||
workflowId: String,
|
||||
|
@ -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")
|
||||
|
@ -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)),
|
||||
),
|
||||
|
@ -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 = "",
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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)),
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -0,0 +1 @@
|
||||
sbt.version=1.9.7
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user