Skip to content

Commit 025cdcf

Browse files
[main] Update 2025-11-18.22 (#403)
Reference commit: f9530839ee
1 parent a3af00c commit 025cdcf

File tree

364 files changed

+16643
-4768
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

364 files changed

+16643
-4768
lines changed

UNRELEASED.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ Template for a bigger topic
3333

3434
## Bugfixes
3535

36+
- Fixed a bug preventing automatic synchronization of protocol feature flags.
37+
Automatic synchronization can be disabled by setting `parameters.auto-sync-protocol-feature-flags = false` in the participant's configuration object.
38+
3639
### (YY-nnn, Severity): Title
3740

3841
#### Issue Description
@@ -69,7 +72,7 @@ The ability to recompute contract ids upon ACS import has been removed.
6972

7073
## Compatibility
7174

72-
The following Canton protocol and Ethereum sequencer contract versions are supported:
75+
The following Canton protocol versions are supported:
7376

7477
| Dependency | Version |
7578
|----------------------------|----------------------------|

community-build.sbt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ lazy val `community-integration-testing` = CommunityProjects.`community-integrat
1414
lazy val `community-integration-testing-lib` = CommunityProjects.`community-integration-testing-lib`
1515
lazy val `daml-script-tests` = CommunityProjects.`daml-script-tests`
1616
lazy val microbench = CommunityProjects.microbench
17+
lazy val `performance-driver` = CommunityProjects.`performance-driver`
18+
lazy val performance = CommunityProjects.performance
1719
lazy val demo = CommunityProjects.demo
1820
lazy val blake2b = CommunityProjects.blake2b
1921
lazy val `slick-fork` = CommunityProjects.`slick-fork`
22+
lazy val `pekko-fork` = CommunityProjects.`pekko-fork`
2023
lazy val `magnolify-addon` = CommunityProjects.`magnolify-addon`
2124
lazy val `scalatest-addon` = CommunityProjects.`scalatest-addon`
2225
lazy val `util-external` = CommunityProjects.`util-external`
@@ -52,15 +55,14 @@ lazy val `ledger-json-client` = CommunityProjects.`ledger-json-client`
5255
lazy val `ledger-api-tools` = CommunityProjects.`ledger-api-tools`
5356
lazy val `ledger-api-string-interning-benchmark` =
5457
CommunityProjects.`ledger-api-string-interning-benchmark`
55-
lazy val `transcode` = CommunityProjects.`transcode`
5658
lazy val `ledger-api-bench-tool` = CommunityProjects.`ledger-api-bench-tool`
5759
lazy val `ledger-test-tool-suites-2-1` = CommunityProjects.`ledger-test-tool-suites-2-1`
5860
lazy val `ledger-test-tool-suites-2-dev` = CommunityProjects.`ledger-test-tool-suites-2-dev`
5961
lazy val `ledger-test-tool-2-1` = CommunityProjects.`ledger-test-tool-2-1`
6062
lazy val `ledger-test-tool-2-dev` = CommunityProjects.`ledger-test-tool-2-dev`
6163
lazy val `conformance-testing` = CommunityProjects.`conformance-testing`
62-
lazy val `enterprise-upgrading-integration-tests` =
63-
CommunityProjects.`enterprise-upgrading-integration-tests`
64+
lazy val `upgrading-integration-tests` =
65+
CommunityProjects.`upgrading-integration-tests`
6466

6567
lazy val root = (project in file("."))
6668
.disablePlugins(WartRemover)

community/admin-api/src/main/protobuf/com/digitalasset/canton/admin/participant/v30/participant_repair_service.proto

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package com.digitalasset.canton.admin.participant.v30;
77

88
import "com/digitalasset/canton/admin/participant/v30/acs_import.proto";
99
import "com/digitalasset/canton/admin/participant/v30/synchronizer_connectivity.proto";
10+
import "com/digitalasset/canton/admin/sequencer/v30/sequencer_connection.proto";
1011
import "google/protobuf/duration.proto";
1112
import "google/protobuf/timestamp.proto";
1213

@@ -60,6 +61,17 @@ service ParticipantRepairService {
6061
rpc RollbackUnassignment(RollbackUnassignmentRequest) returns (RollbackUnassignmentResponse);
6162

6263
rpc RepairCommitmentsUsingAcs(RepairCommitmentsUsingAcsRequest) returns (RepairCommitmentsUsingAcsResponse);
64+
65+
/**
66+
Perform a logical synchronizer upgrade
67+
This endpoint should ONLY be used when the following two conditions are met:
68+
- The participant node missed the upgrade on the old synchronizer, and
69+
- The old synchronizer has been decommissioned in the meantime.
70+
71+
After the upgrade has been done, other repair operations might need to be done.
72+
This includes manually repairing the ACS to account for missed activity on both the old and new synchronizer.
73+
*/
74+
rpc PerformSynchronizerUpgrade(PerformSynchronizerUpgradeRequest) returns (PerformSynchronizerUpgradeResponse);
6375
}
6476

6577
message PurgeContractsRequest {
@@ -349,3 +361,21 @@ message RepairCommitmentsStatus {
349361
google.protobuf.Timestamp completed_repair_timestamp = 3;
350362
}
351363
}
364+
365+
message PerformSynchronizerUpgradeRequest {
366+
message Successor {
367+
string physical_synchronizer_id = 1;
368+
369+
// Value should be provided by synchronizer owners
370+
google.protobuf.Timestamp announced_upgrade_time = 2;
371+
372+
SynchronizerConnectionConfig config = 3;
373+
374+
com.digitalasset.canton.admin.sequencer.v30.SequencerConnectionValidation sequencer_connection_validation = 4;
375+
}
376+
377+
string physical_synchronizer_id = 1;
378+
Successor successor = 2;
379+
}
380+
381+
message PerformSynchronizerUpgradeResponse {}

community/admin-api/src/main/protobuf/com/digitalasset/canton/admin/participant/v30/enterprise_participant_replication_service.proto renamed to community/admin-api/src/main/protobuf/com/digitalasset/canton/admin/participant/v30/participant_replication_service.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ syntax = "proto3";
55

66
package com.digitalasset.canton.admin.participant.v30;
77

8-
service EnterpriseParticipantReplicationService {
8+
service ParticipantReplicationService {
99
rpc SetPassive(SetPassiveRequest) returns (SetPassiveResponse);
1010
}
1111

community/admin-api/src/main/protobuf/com/digitalasset/canton/admin/participant/v30/party_management_service.proto

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,23 @@ service PartyManagementService {
7070

7171
// Offline party replication endpoint.
7272
//
73-
// Instructs the target participant to unilaterally clear the 'onboarding' flag on the
74-
// party-to-participant topology mapping.
73+
// Instructs the participant to unilaterally clear the 'onboarding' flag for a
74+
// given party on its party-to-participant topology mapping.
7575
//
76-
// This operation is time-sensitive and will only be attempted after a specific safe
77-
// timestamp has passed.
76+
// The RPC first attempts to clear the flag immediately. If this is not yet safe
77+
// (e.g., due to potential in-flight transactions), it schedules an idempotent
78+
// background task to propose the clearance at the appropriate, safe time.
7879
//
79-
// Because the effect of the topology transaction is not instantaneous, this endpoint is
80-
// designed to be polled. Callers should invoke it repeatedly until the response confirms
81-
// the flag has been cleared.
80+
// This RPC is idempotent and designed to be polled.
8281
//
83-
// Prerequisite: A prior party-to-participant mapping topology transaction must exist
84-
// that activates the party on the target participant with the onboarding flag set to
85-
// `true`.
82+
// Response status:
83+
// - onboarded = true: The flag is successfully cleared or was already clear.
84+
// - onboarded = false: The flag is not yet cleared. A background task is scheduled
85+
// (or was already pending).
86+
//
87+
// Prerequisites:
88+
// - An 'onboarding=true' mapping must exist for the party on this participant.
89+
// - The participant must be connected to the requested synchronizer.
8690
rpc ClearPartyOnboardingFlag(ClearPartyOnboardingFlagRequest) returns (ClearPartyOnboardingFlagResponse);
8791
}
8892

@@ -325,6 +329,13 @@ message GetHighestOffsetByTimestampResponse {
325329
int64 ledger_offset = 1;
326330
}
327331

332+
// Request to clear the 'onboarding' flag for a party.
333+
//
334+
// The participant uses the `begin_offset_exclusive` and `wait_for_activation_timeout` to find the
335+
// effective timestamp of the topology transaction that set the `onboarding = true` flag.
336+
//
337+
// This effective timestamp serves as the "lower bound" for calculating the "latest decision deadline"
338+
// which determines the earliest safe time to clear the flag.
328339
message ClearPartyOnboardingFlagRequest {
329340
// The identifier of the party whose onboarding flag is being cleared. This party must
330341
// already be active on the target participant.
@@ -347,8 +358,10 @@ message ClearPartyOnboardingFlagRequest {
347358
google.protobuf.Duration wait_for_activation_timeout = 4;
348359
}
349360

350-
// Responds with the current status:
361+
// Responds with the current status of the onboarding flag:
362+
//
351363
// - Cleared: (true, None) – The flag is successfully cleared.
364+
//
352365
// - Pending: (false, Some(timestamp)) – The flag is still set. The timestamp indicates
353366
// the earliest safe time to clear the flag.
354367
// Calling the service (repeatedly) after that specific time will eventually confirm
@@ -364,6 +377,8 @@ message ClearPartyOnboardingFlagResponse {
364377

365378
// The earliest time for the safe clearance of the onboarding flag.
366379
//
367-
// This field is only present when `onboarded` is `false`.
380+
// This field is only present when `onboarded` is `false`. It represents the
381+
// computed "latest decision deadline", which is the maximum of all `validUntil + decisionTimeout`
382+
// from the synchronizer's parameter history, ensuring all past in-flight transactions are finalized.
368383
optional google.protobuf.Timestamp earliest_retry_timestamp = 2;
369384
}

community/app-base/src/main/scala/com/digitalasset/canton/admin/api/client/commands/ParticipantAdminCommands.scala

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import com.digitalasset.canton.admin.api.client.data.{
2020
PackageDescription,
2121
ParticipantPruningSchedule,
2222
ParticipantStatus,
23+
PartyOnboardingFlagStatus,
2324
}
2425
import com.digitalasset.canton.admin.participant.v30
25-
import com.digitalasset.canton.admin.participant.v30.EnterpriseParticipantReplicationServiceGrpc.EnterpriseParticipantReplicationServiceStub
2626
import com.digitalasset.canton.admin.participant.v30.PackageServiceGrpc.PackageServiceStub
2727
import com.digitalasset.canton.admin.participant.v30.ParticipantInspectionServiceGrpc.ParticipantInspectionServiceStub
2828
import com.digitalasset.canton.admin.participant.v30.ParticipantRepairServiceGrpc.ParticipantRepairServiceStub
29+
import com.digitalasset.canton.admin.participant.v30.ParticipantReplicationServiceGrpc.ParticipantReplicationServiceStub
2930
import com.digitalasset.canton.admin.participant.v30.ParticipantStatusServiceGrpc.ParticipantStatusServiceStub
3031
import com.digitalasset.canton.admin.participant.v30.PartyManagementServiceGrpc.PartyManagementServiceStub
3132
import com.digitalasset.canton.admin.participant.v30.PingServiceGrpc.PingServiceStub
@@ -691,7 +692,7 @@ object ParticipantAdminCommands {
691692
) extends GrpcAdminCommand[
692693
v30.ClearPartyOnboardingFlagRequest,
693694
v30.ClearPartyOnboardingFlagResponse,
694-
(Boolean, Option[CantonTimestamp]),
695+
PartyOnboardingFlagStatus,
695696
] {
696697

697698
override type Svc = PartyManagementServiceStub
@@ -716,16 +717,9 @@ object ParticipantAdminCommands {
716717

717718
override protected def handleResponse(
718719
response: v30.ClearPartyOnboardingFlagResponse
719-
): Either[String, (Boolean, Option[CantonTimestamp])] =
720-
response.earliestRetryTimestamp
721-
.traverse(
722-
CantonTimestamp
723-
.fromProtoTimestamp(_)
724-
.leftMap(_.message)
725-
)
726-
.map(tsOption => (response.onboarded, tsOption))
720+
): Either[String, PartyOnboardingFlagStatus] =
721+
PartyOnboardingFlagStatus.fromProtoV30(response).leftMap(_.message)
727722
}
728-
729723
}
730724

731725
object ParticipantRepairManagement {
@@ -1163,6 +1157,49 @@ object ParticipantAdminCommands {
11631157
response: v30.RollbackUnassignmentResponse
11641158
): Either[String, Unit] = Either.unit
11651159
}
1160+
1161+
final case class PerformSynchronizerUpgrade(
1162+
currentPSId: PhysicalSynchronizerId,
1163+
successorPSId: PhysicalSynchronizerId,
1164+
upgradeTime: CantonTimestamp,
1165+
successorConfig: SynchronizerConnectionConfig,
1166+
sequencerConnectionValidation: SequencerConnectionValidation,
1167+
) extends GrpcAdminCommand[
1168+
v30.PerformSynchronizerUpgradeRequest,
1169+
v30.PerformSynchronizerUpgradeResponse,
1170+
Unit,
1171+
] {
1172+
override type Svc = ParticipantRepairServiceStub
1173+
1174+
override def createService(channel: ManagedChannel): ParticipantRepairServiceStub =
1175+
v30.ParticipantRepairServiceGrpc.stub(channel)
1176+
1177+
override protected def createRequest()
1178+
: Either[String, v30.PerformSynchronizerUpgradeRequest] =
1179+
Right(
1180+
v30.PerformSynchronizerUpgradeRequest(
1181+
physicalSynchronizerId = currentPSId.toProtoPrimitive,
1182+
successor = v30.PerformSynchronizerUpgradeRequest
1183+
.Successor(
1184+
physicalSynchronizerId = successorPSId.toProtoPrimitive,
1185+
announcedUpgradeTime = upgradeTime.toProtoTimestamp.some,
1186+
config = successorConfig.toProtoV30.some,
1187+
sequencerConnectionValidation = sequencerConnectionValidation.toProtoV30,
1188+
)
1189+
.some,
1190+
)
1191+
)
1192+
1193+
override protected def submitRequest(
1194+
service: ParticipantRepairServiceStub,
1195+
request: v30.PerformSynchronizerUpgradeRequest,
1196+
): Future[v30.PerformSynchronizerUpgradeResponse] =
1197+
service.performSynchronizerUpgrade(request)
1198+
1199+
override protected def handleResponse(
1200+
response: v30.PerformSynchronizerUpgradeResponse
1201+
): Either[String, Unit] = Either.unit
1202+
}
11661203
}
11671204

11681205
object Ping {
@@ -2436,18 +2473,18 @@ object ParticipantAdminCommands {
24362473

24372474
final case class SetPassiveCommand()
24382475
extends GrpcAdminCommand[v30.SetPassiveRequest, v30.SetPassiveResponse, Unit] {
2439-
override type Svc = EnterpriseParticipantReplicationServiceStub
2476+
override type Svc = ParticipantReplicationServiceStub
24402477

24412478
override def createService(
24422479
channel: ManagedChannel
2443-
): EnterpriseParticipantReplicationServiceStub =
2444-
v30.EnterpriseParticipantReplicationServiceGrpc.stub(channel)
2480+
): ParticipantReplicationServiceStub =
2481+
v30.ParticipantReplicationServiceGrpc.stub(channel)
24452482

24462483
override protected def createRequest(): Either[String, v30.SetPassiveRequest] =
24472484
Right(v30.SetPassiveRequest())
24482485

24492486
override protected def submitRequest(
2450-
service: EnterpriseParticipantReplicationServiceStub,
2487+
service: ParticipantReplicationServiceStub,
24512488
request: v30.SetPassiveRequest,
24522489
): Future[v30.SetPassiveResponse] =
24532490
service.setPassive(request)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package com.digitalasset.canton.admin.api.client.data
5+
6+
import cats.implicits.toTraverseOps
7+
import com.digitalasset.canton.ProtoDeserializationError.OtherError
8+
import com.digitalasset.canton.admin.participant.v30
9+
import com.digitalasset.canton.data.CantonTimestamp
10+
import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting}
11+
import com.digitalasset.canton.serialization.ProtoConverter.ParsingResult
12+
13+
sealed trait PartyOnboardingFlagStatus extends PrettyPrinting {
14+
protected val status: (Boolean, Option[CantonTimestamp])
15+
}
16+
17+
case object FlagNotSet extends PartyOnboardingFlagStatus {
18+
override val status: (Boolean, Option[CantonTimestamp]) = (true, None)
19+
20+
override protected def pretty: Pretty[FlagNotSet.type] = prettyOfObject[FlagNotSet.type]
21+
}
22+
23+
final case class FlagSet(safeToClear: CantonTimestamp) extends PartyOnboardingFlagStatus {
24+
override val status: (Boolean, Option[CantonTimestamp]) = (false, Some(safeToClear))
25+
26+
override protected def pretty: Pretty[FlagSet] = prettyOfClass(
27+
param("earliest safe time to clear the flag", _.safeToClear)
28+
)
29+
}
30+
31+
object PartyOnboardingFlagStatus {
32+
33+
def fromProtoV30(
34+
proto: v30.ClearPartyOnboardingFlagResponse
35+
): ParsingResult[PartyOnboardingFlagStatus] =
36+
proto.earliestRetryTimestamp
37+
.traverse(CantonTimestamp.fromProtoTimestamp)
38+
.flatMap { tsOption =>
39+
(proto.onboarded, tsOption) match {
40+
case FlagNotSet.status => Right(FlagNotSet)
41+
case (false, Some(ts)) => Right(FlagSet(ts))
42+
case (onboarded, ts) =>
43+
Left(
44+
OtherError(
45+
s"Invalid PartyOnboardingFlagStatus: onboarded=$onboarded, earliest_retry_timestamp=$ts"
46+
)
47+
)
48+
}
49+
}
50+
51+
}

community/app-base/src/main/scala/com/digitalasset/canton/config/CantonConfig.scala

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -459,15 +459,8 @@ final case class CantonConfig(
459459
private def validate(
460460
edition: CantonEdition,
461461
ensurePortsSet: Boolean,
462-
): Validated[NonEmpty[Seq[String]], Unit] = {
463-
val validator = edition match {
464-
case CommunityCantonEdition =>
465-
CommunityConfigValidations
466-
case EnterpriseCantonEdition =>
467-
EnterpriseConfigValidations
468-
}
469-
validator.validate(this, edition, ensurePortsSet = ensurePortsSet)
470-
}
462+
): Validated[NonEmpty[Seq[String]], Unit] =
463+
CommunityConfigValidations.validate(this, edition, ensurePortsSet = ensurePortsSet)
471464

472465
private lazy val participantNodeParameters_ : Map[InstanceName, ParticipantNodeParameters] =
473466
participants.fmap { participantConfig =>
@@ -500,6 +493,7 @@ final case class CantonConfig(
500493
participantParameters.doNotAwaitOnCheckingIncomingCommitments,
501494
disableOptionalTopologyChecks = participantConfig.topology.disableOptionalTopologyChecks,
502495
commitmentCheckpointInterval = participantParameters.commitmentCheckpointInterval,
496+
autoSyncProtocolFeatureFlags = participantParameters.autoSyncProtocolFeatureFlags,
503497
)
504498
}
505499

@@ -522,6 +516,8 @@ final case class CantonConfig(
522516
maxConfirmationRequestsBurstFactor =
523517
sequencerNodeConfig.parameters.maxConfirmationRequestsBurstFactor,
524518
asyncWriter = sequencerNodeConfig.parameters.asyncWriter.toParameters,
519+
sequencingTimeLowerBoundExclusive =
520+
sequencerNodeConfig.parameters.sequencingTimeLowerBoundExclusive,
525521
unsafeEnableOnlinePartyReplication =
526522
sequencerNodeConfig.parameters.unsafeEnableOnlinePartyReplication,
527523
requestLimits = sequencerNodeConfig.publicApi.limits,

0 commit comments

Comments
 (0)