Skip to content

Commit f5b66cf

Browse files
Bump Canton fork to 2025-06-23 (#1192)
* Undo our changes Signed-off-by: Moritz Kiefer <[email protected]> * Bump Canton commit Signed-off-by: Moritz Kiefer <[email protected]> * Reapply our changes Signed-off-by: Moritz Kiefer <[email protected]> * Resolve conflicts [ci] Signed-off-by: Moritz Kiefer <[email protected]> * bump commit [ci] Signed-off-by: Moritz Kiefer <[email protected]> * add headers [ci] Signed-off-by: Moritz Kiefer <[email protected]> --------- Signed-off-by: Moritz Kiefer <[email protected]> Co-authored-by: Moritz Kiefer <[email protected]>
1 parent 0c2232a commit f5b66cf

File tree

180 files changed

+5395
-1513
lines changed

Some content is hidden

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

180 files changed

+5395
-1513
lines changed

MAINTENANCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Initial setup:
3939
1. Check out the [Canton **Open Source** repo](https://github.com/digital-asset/canton)
4040
2. Define the environment variable used in the commands below using `export PATH_TO_CANTON_OSS=<your-canton-oss-repo-path>`. This can be added to your private env vars.
4141

42-
Current Canton commit: `36118b5abce6d21ab6cd8d47daeff8a1c584132b`
42+
Current Canton commit: `a0f2007fa5c9f9ecf1d58a4be564ceaa3158ebb3`
4343

4444
1. Checkout the **current Canton commit listed above** in the Canton open source repo from above, so we can diff our current fork against this checkout.
4545
2. Change to your checkout of the Splice repo and execute the following steps:

canton/UNRELEASED.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ schedule, i.e. if you add an entry effective at or after the first
99
header, prepend the new date header that corresponds to the
1010
Wednesday after your change.
1111

12+
## Until 2025-06-25 (Exclusive)
13+
- Adds new gRPC endpoint `GetHighestOffsetByTimestamp` (and console command `find_highest_offset_by_timestamp`) that
14+
for a given timestamp, finds the highest ledger offset among all events that have record time <= timestamp. This is a
15+
backward-compatible change, because it's an addition only. It's useful for party replication / major upgrade.
16+
17+
## Until 2025-06-11 (Exclusive)
18+
- Dead parameter `canton.participants.<participant>.http-ledger-api.allow-insecure-tokens` has been removed.
19+
20+
## Until 2025-06-04 (Exclusive)
21+
- **Breaking** The console command `connect_local_bft` takes now a list of `SequencerReference` instead of a `NonEmpty[Map[SequencerAlias, SequencerReference]]`
22+
- Console command - A new console command `connect_bft` has been added to connect by url to Decentralized Sequencers
23+
-
1224
## Until 2025-05-14 (Exclusive)
1325
- JSON - changes in openapi (`Any` renamed as `ProtoAny`, `Event1` renamed to `TopologyEvent` and fixed, fixed `Field`, `FieldMask`,`JsReassignmentEvent` mappings.
1426
- JSON API - fixed openapi documentation for maps: (`eventsById`,`filtersByParty`).

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ service PartyManagementService {
2828
// successfully.
2929
rpc GetAddPartyStatus(GetAddPartyStatusRequest) returns (GetAddPartyStatusResponse);
3030

31-
// Export the ACS for the given parties from the participant
31+
// Export the ACS for the given parties from the participant.
3232
rpc ExportAcs(ExportAcsRequest) returns (stream ExportAcsResponse);
3333

34-
// Export the ACS for the given parties at a timestamp (that is the effective time of a topology transaction)
34+
// Export the ACS for the given parties at a timestamp (that is the effective time of a topology transaction).
3535
rpc ExportAcsAtTimestamp(ExportAcsAtTimestampRequest) returns (stream ExportAcsAtTimestampResponse);
36+
37+
// For a given timestamp, find the highest ledger offset among all events that have record time <= timestamp.
38+
//
39+
// Returns a ledger offset, or an error otherwise. Depending on the error cause, a retry may make sense.
40+
// Retryable errors are defined as: OUT_OF_RANGE/INVALID_TIMESTAMP_PARTY_MANAGEMENT_ERROR.
41+
// Further, a returned offset is guaranteed to be "clean", meaning all events have been processed fully and
42+
// published to the Ledger API DB until the requested timestamp.
43+
rpc GetHighestOffsetByTimestamp(GetHighestOffsetByTimestampRequest) returns (GetHighestOffsetByTimestampResponse);
3644
}
3745

3846
message AddPartyAsyncRequest {
@@ -213,3 +221,29 @@ message ExportAcsAtTimestampResponse {
213221
// Required
214222
bytes chunk = 1;
215223
}
224+
225+
// Requests the highest ledger offset among all events belonging to the synchronizer (`synchronizer_id`)
226+
// that have a record time before or at the given `timestamp`.
227+
//
228+
// This endpoint features a `force` message field. This is intended for disaster recovery scenarios only.
229+
message GetHighestOffsetByTimestampRequest {
230+
// The identifier of the synchronizer.
231+
// Required
232+
string synchronizer_id = 1;
233+
234+
// The requested timestamp for which a ledger offset should be found.
235+
// Required
236+
google.protobuf.Timestamp timestamp = 2;
237+
238+
// If true, the ledger end offset is requested instead of the highest ledger offset among events
239+
// with a record time at or before the requested `timestamp`.
240+
// Required, defaults to false.
241+
bool force = 3;
242+
}
243+
244+
message GetHighestOffsetByTimestampResponse {
245+
// The highest ledger offset among events that have their record time before or at the requested timestamp.
246+
// An error when no such offset (yet) exists.
247+
// Required
248+
int64 ledger_offset = 1;
249+
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,42 @@ object ParticipantAdminCommands {
537537
): Either[String, AddPartyStatus] = AddPartyStatus.fromProtoV30(response).leftMap(_.toString)
538538
}
539539

540+
final case class GetHighestOffsetByTimestamp(
541+
synchronizerId: SynchronizerId,
542+
timestamp: Instant,
543+
force: Boolean,
544+
) extends GrpcAdminCommand[
545+
v30.GetHighestOffsetByTimestampRequest,
546+
v30.GetHighestOffsetByTimestampResponse,
547+
NonNegativeLong,
548+
] {
549+
override type Svc = PartyManagementServiceStub
550+
551+
override def createService(channel: ManagedChannel): PartyManagementServiceStub =
552+
v30.PartyManagementServiceGrpc.stub(channel)
553+
554+
override protected def createRequest()
555+
: Either[String, v30.GetHighestOffsetByTimestampRequest] =
556+
Right(
557+
v30.GetHighestOffsetByTimestampRequest(
558+
synchronizerId.toProtoPrimitive,
559+
Some(Timestamp(timestamp)),
560+
force,
561+
)
562+
)
563+
564+
override protected def submitRequest(
565+
service: PartyManagementServiceStub,
566+
request: v30.GetHighestOffsetByTimestampRequest,
567+
): Future[v30.GetHighestOffsetByTimestampResponse] =
568+
service.getHighestOffsetByTimestamp(request)
569+
570+
override protected def handleResponse(
571+
response: v30.GetHighestOffsetByTimestampResponse
572+
): Either[String, NonNegativeLong] =
573+
NonNegativeLong.create(response.ledgerOffset).leftMap(_.toString)
574+
}
575+
540576
final case class ExportAcs(
541577
parties: Set[PartyId],
542578
filterSynchronizerId: Option[SynchronizerId],

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ import com.digitalasset.canton.topology.*
2020
import com.digitalasset.canton.topology.admin.grpc.{BaseQuery, TopologyStoreId}
2121
import com.digitalasset.canton.topology.admin.v30
2222
import com.digitalasset.canton.topology.admin.v30.*
23-
import com.digitalasset.canton.topology.admin.v30.AuthorizeRequest.Type.{
24-
Proposal,
25-
TransactionHashBytes,
26-
}
23+
import com.digitalasset.canton.topology.admin.v30.AuthorizeRequest.Type.{Proposal, TransactionHash}
2724
import com.digitalasset.canton.topology.admin.v30.IdentityInitializationServiceGrpc.IdentityInitializationServiceStub
2825
import com.digitalasset.canton.topology.admin.v30.TopologyAggregationServiceGrpc.TopologyAggregationServiceStub
2926
import com.digitalasset.canton.topology.admin.v30.TopologyManagerReadServiceGrpc.TopologyManagerReadServiceStub
@@ -915,7 +912,7 @@ object TopologyAdminCommands {
915912
}
916913

917914
final case class Authorize[M <: TopologyMapping: ClassTag](
918-
transactionHash: ByteString,
915+
transactionHash: String,
919916
mustFullyAuthorize: Boolean,
920917
signedBy: Seq[Fingerprint],
921918
store: TopologyStoreId,
@@ -928,7 +925,7 @@ object TopologyAdminCommands {
928925

929926
override protected def createRequest(): Either[String, AuthorizeRequest] = Right(
930927
AuthorizeRequest(
931-
TransactionHashBytes(transactionHash),
928+
TransactionHash(transactionHash),
932929
mustFullyAuthorize = mustFullyAuthorize,
933930
forceChanges = Seq.empty,
934931
signedBy = signedBy.map(_.toProtoPrimitive),

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ object CommunityConfigValidations extends ConfigValidations with NamedLogging {
7777
eitherUserListsOrPrivilegedTokensOnParticipants,
7878
sessionSigningKeysOnlyWithKms,
7979
distinctScopesAndAudiencesOnAuthServices,
80+
engineAdditionalConsistencyChecksParticipants,
8081
)
8182

8283
/** Group node configs by db access to find matching db storage configs. Overcomplicated types
@@ -250,6 +251,19 @@ object CommunityConfigValidations extends ConfigValidations with NamedLogging {
250251
toValidated(errors)
251252
}
252253

254+
private def engineAdditionalConsistencyChecksParticipants(
255+
config: CantonConfig
256+
): Validated[NonEmpty[Seq[String]], Unit] = {
257+
val errors = config.participants.toSeq.mapFilter { case (name, participantConfig) =>
258+
Option.when(
259+
participantConfig.parameters.engine.enableAdditionalConsistencyChecks && !config.parameters.nonStandardConfig
260+
)(
261+
s"Enabling additional consistency checks on the Daml Engine for participant ${name.unwrap} requires to explicitly set canton.parameters.non-standard-config = true"
262+
)
263+
}
264+
toValidated(errors)
265+
}
266+
253267
private def sessionSigningKeysOnlyWithKms(
254268
config: CantonConfig
255269
): Validated[NonEmpty[Seq[String]], Unit] = {

canton/community/app-base/src/main/scala/com/digitalasset/canton/console/AmmoniteCacheLock.scala

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -90,43 +90,37 @@ object AmmoniteCacheLock {
9090
): Either[Throwable, Option[AmmoniteCacheLock]] = blocking(synchronized {
9191
try {
9292
val myLockFile = path / "lock"
93-
if (myLockFile.toIO.exists()) {
94-
Right(None)
95-
} else {
96-
logger.debug(s"Attempting to obtain lock $myLockFile")
97-
val out = new RandomAccessFile(myLockFile.toIO, "rw")
98-
Option(out.getChannel.tryLock()) match {
99-
case None =>
100-
logger.debug(s"Failed to acquire lock for $myLockFile")
101-
out.close()
102-
Right(None)
103-
case Some(lock) =>
104-
myLockFile.toIO.deleteOnExit()
105-
Right(Some(new AmmoniteCacheLock {
106-
override def release(): Unit =
107-
try {
108-
logger.debug(s"Releasing lock $myLockFile...")
109-
lock.release()
110-
out.close()
111-
if (!Files.deleteIfExists(myLockFile.toNIO)) { // throws when the file cannot be deleted
112-
logger.warn(s"Failed to delete lock file $myLockFile because it did not exist")
113-
}
114-
} catch {
115-
case NonFatal(e) =>
116-
logger.error(s"Releasing ammonite cache lock $lockFile failed", e)
93+
logger.debug(s"Attempting to obtain lock $myLockFile")
94+
val out = new RandomAccessFile(myLockFile.toIO, "rw")
95+
Option(out.getChannel.tryLock()) match {
96+
case None =>
97+
logger.debug(s"Failed to acquire lock for $myLockFile")
98+
out.close()
99+
Right(None)
100+
case Some(lock) =>
101+
Right(Some(new AmmoniteCacheLock {
102+
override def release(): Unit =
103+
try {
104+
logger.debug(s"Releasing lock $myLockFile...")
105+
lock.release()
106+
out.close()
107+
if (!Files.deleteIfExists(myLockFile.toNIO)) { // throws when the file exists but cannot be deleted
108+
logger.warn(s"Failed to delete lock file $myLockFile because it did not exist")
117109
}
110+
} catch {
111+
case NonFatal(e) =>
112+
logger.error(s"Releasing ammonite cache lock $lockFile failed", e)
113+
}
118114

119-
override val storage: Storage = new Storage.Folder(path, isRepl = isRepl)
115+
override val storage: Storage = new Storage.Folder(path, isRepl = isRepl)
120116

121-
override def toString: String = s"file cache at $path"
117+
override def toString: String = s"file cache at $path"
122118

123-
override def lockFile: Option[File] = Some(myLockFile.toIO)
124-
}))
125-
126-
}
119+
override def lockFile: Option[File] = Some(myLockFile.toIO)
120+
}))
127121
}
128122
} catch {
129-
case e: OverlappingFileLockException => Right(None)
123+
case _: OverlappingFileLockException => Right(None)
130124
case NonFatal(e) => Left(e)
131125
}
132126
})

canton/community/app-base/src/main/scala/com/digitalasset/canton/console/ConsoleEnvironment.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import com.digitalasset.canton.time.SimClock
3131
import com.digitalasset.canton.topology.admin.grpc.TopologyStoreId
3232
import com.digitalasset.canton.topology.{ParticipantId, PartyId, SynchronizerId}
3333
import com.digitalasset.canton.tracing.{NoTracing, TraceContext}
34-
import com.digitalasset.canton.{LfPartyId, SynchronizerAlias}
34+
import com.digitalasset.canton.{LfPartyId, SequencerAlias, SynchronizerAlias}
3535
import com.digitalasset.daml.lf.data.Ref.PackageId
3636
import com.typesafe.scalalogging.Logger
3737
import io.opentelemetry.api.trace.Tracer
@@ -570,6 +570,9 @@ object ConsoleEnvironment {
570570
implicit def toGrpcSequencerConnection(connection: String): SequencerConnection =
571571
GrpcSequencerConnection.tryCreate(connection)
572572

573+
implicit def toSequencerAlias(alias: String): SequencerAlias =
574+
SequencerAlias.tryCreate(alias)
575+
573576
implicit def toSequencerConnections(connection: String): SequencerConnections =
574577
SequencerConnections.single(GrpcSequencerConnection.tryCreate(connection))
575578

canton/community/app-base/src/main/scala/com/digitalasset/canton/console/ConsoleEnvironmentBinding.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ object ConsoleEnvironmentBinding {
8181
|import com.digitalasset.canton.sequencing.SequencerConnection
8282
|import com.digitalasset.canton.sequencing.SequencerConnections
8383
|import com.digitalasset.canton.sequencing.SequencerConnectionValidation._
84+
|import com.digitalasset.canton.sequencing.SubmissionRequestAmplification
8485
|import com.digitalasset.canton.sequencing.GrpcSequencerConnection
8586
|""".stripMargin
8687

0 commit comments

Comments
 (0)