diff --git a/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/config/CantonConfig.scala b/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/config/CantonConfig.scala index 03fb43ad7948..473691e2ebf9 100644 --- a/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/config/CantonConfig.scala +++ b/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/config/CantonConfig.scala @@ -716,6 +716,9 @@ object CantonConfig { deriveReader[LedgerApiKeepAliveServerConfig] lazy implicit val tlsServerConfigReader: ConfigReader[TlsServerConfig] = deriveReader[TlsServerConfig] + lazy implicit val tlsServerOnlyTrustFileConfigReader + : ConfigReader[TlsClientConfigOnlyTrustFile] = + deriveReader[TlsClientConfigOnlyTrustFile] lazy implicit val tlsClientConfigReader: ConfigReader[TlsClientConfig] = deriveReader[TlsClientConfig] lazy implicit val initBaseIdentityConfigReader: ConfigReader[InitConfigBase.Identity] = @@ -764,6 +767,21 @@ object CantonConfig { deriveReader[ApiType.Grpc.type] lazy implicit val apiTypeConfigReader: ConfigReader[ApiType] = deriveReader[ApiType] lazy implicit val clientConfigReader: ConfigReader[ClientConfig] = deriveReader[ClientConfig] + lazy implicit val remoteConsoleSequencerClientConfigReader + : ConfigReader[SequencerApiClientConfig] = + deriveReader[SequencerApiClientConfig] + // Note that transformations are applied in reverse order, + // so we move the deprecated before deleting the parent path + .deprecateConfigPath( + DeprecatedConfigUtils.DeprecatedConfigPath[String]( + "custom-trust-certificates", + since = "2.10.0", + dropPath = true, + ) + ) + .moveDeprecatedField("custom-trust-certificates.pem-file", Seq("tls.trust-collection-file")) + .moveDeprecatedField("transport-security", Seq("tls.enabled")) + lazy implicit val remoteDomainConfigReader: ConfigReader[RemoteDomainConfig] = deriveReader[RemoteDomainConfig] lazy implicit val remoteParticipantConfigReader: ConfigReader[RemoteParticipantConfig] = @@ -863,22 +881,6 @@ object CantonConfig { deriveReader[PackageMetadataViewConfig] lazy implicit val identityConfigReader: ConfigReader[TopologyConfig] = deriveReader[TopologyConfig] - lazy implicit val sequencerConnectionConfigCertificateFileReader - : ConfigReader[SequencerConnectionConfig.CertificateFile] = - deriveReader[SequencerConnectionConfig.CertificateFile] - lazy implicit val sequencerConnectionConfigCertificateStringReader - : ConfigReader[SequencerConnectionConfig.CertificateString] = - deriveReader[SequencerConnectionConfig.CertificateString] - lazy implicit val sequencerConnectionConfigCertificateConfigReader - : ConfigReader[SequencerConnectionConfig.CertificateConfig] = - deriveReader[SequencerConnectionConfig.CertificateConfig] - lazy implicit val sequencerConnectionConfigGrpcReader - : ConfigReader[SequencerConnectionConfig.Grpc] = - deriveReader[SequencerConnectionConfig.Grpc] - lazy implicit val sequencerConnectionConfigReader: ConfigReader[SequencerConnectionConfig] = - deriveReader[SequencerConnectionConfig] - // since the big majority of users will use GRPC, default to it so that they don't need to specify `type = grpc` - .orElse(ConfigReader[SequencerConnectionConfig.Grpc]) lazy implicit val communitySequencerConfigDatabaseReader : ConfigReader[CommunitySequencerConfig.Database] = deriveReader[CommunitySequencerConfig.Database] @@ -1126,6 +1128,9 @@ object CantonConfig { deriveWriter[TlsServerConfig] lazy implicit val tlsClientConfigWriter: ConfigWriter[TlsClientConfig] = deriveWriter[TlsClientConfig] + lazy implicit val tlsClientConfigWriterOnlyTrustFile + : ConfigWriter[TlsClientConfigOnlyTrustFile] = + deriveWriter[TlsClientConfigOnlyTrustFile] lazy implicit val initBaseIdentityConfigWriter: ConfigWriter[InitConfigBase.Identity] = deriveWriter[InitConfigBase.Identity] lazy implicit val initConfigWriter: ConfigWriter[InitConfig] = deriveWriter[InitConfig] @@ -1166,6 +1171,9 @@ object CantonConfig { lazy implicit val communityCryptoWriter: ConfigWriter[CommunityCryptoConfig] = deriveWriter[CommunityCryptoConfig] lazy implicit val clientConfigWriter: ConfigWriter[ClientConfig] = deriveWriter[ClientConfig] + lazy implicit val remoteConsoleSequencerClientConfigWriter + : ConfigWriter[SequencerApiClientConfig] = + deriveWriter[SequencerApiClientConfig] lazy implicit val remoteDomainConfigWriter: ConfigWriter[RemoteDomainConfig] = deriveWriter[RemoteDomainConfig] lazy implicit val remoteParticipantConfigWriter: ConfigWriter[RemoteParticipantConfig] = @@ -1272,20 +1280,6 @@ object CantonConfig { deriveWriter[PackageMetadataViewConfig] lazy implicit val identityConfigWriter: ConfigWriter[TopologyConfig] = deriveWriter[TopologyConfig] - lazy implicit val sequencerConnectionConfigCertificateFileWriter - : ConfigWriter[SequencerConnectionConfig.CertificateFile] = - deriveWriter[SequencerConnectionConfig.CertificateFile] - lazy implicit val sequencerConnectionConfigCertificateStringWriter - : ConfigWriter[SequencerConnectionConfig.CertificateString] = - confidentialWriter[SequencerConnectionConfig.CertificateString](_.copy(pemString = "****")) - lazy implicit val sequencerConnectionConfigCertificateConfigWriter - : ConfigWriter[SequencerConnectionConfig.CertificateConfig] = - deriveWriter[SequencerConnectionConfig.CertificateConfig] - lazy implicit val sequencerConnectionConfigGrpcWriter - : ConfigWriter[SequencerConnectionConfig.Grpc] = - deriveWriter[SequencerConnectionConfig.Grpc] - lazy implicit val sequencerConnectionConfigWriter: ConfigWriter[SequencerConnectionConfig] = - deriveWriter[SequencerConnectionConfig] lazy implicit val communitySequencerConfigDatabaseWriter : ConfigWriter[CommunitySequencerConfig.Database] = deriveWriter[CommunitySequencerConfig.Database] diff --git a/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/console/InstanceReference.scala b/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/console/InstanceReference.scala index a3914ad43483..f1bb3256b035 100644 --- a/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/console/InstanceReference.scala +++ b/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/console/InstanceReference.scala @@ -315,7 +315,7 @@ trait RemoteDomainReference extends DomainReference with GrpcRemoteInstanceRefer consoleEnvironment.environment.config.remoteDomainsByString(name) override def sequencerConnection: SequencerConnection = - config.publicApi.toConnection + config.publicApi.toGrpcSequencerConnection .fold( err => sys.error(s"Domain $name has invalid sequencer connection config: $err"), identity, @@ -354,7 +354,7 @@ trait LocalDomainReference consoleEnvironment.environment.config.domainsByString(name) override def sequencerConnection: SequencerConnection = - config.sequencerConnectionConfig.toConnection + config.sequencerConnectionConfig.toGrpcSequencerConnection .fold( err => sys.error(s"Domain $name has invalid sequencer connection config: $err"), identity, @@ -428,7 +428,7 @@ object ExternalLedgerApiClient { new ExternalLedgerApiClient( cc.address, cc.port, - cc.tls, + cc.tlsConfig, Some(token), ) } diff --git a/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/environment/Environment.scala b/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/environment/Environment.scala index 3624a24b9432..93a3a59c0839 100644 --- a/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/environment/Environment.scala +++ b/sdk/canton/community/app-base/src/main/scala/com/digitalasset/canton/environment/Environment.scala @@ -297,7 +297,7 @@ trait Environment extends NamedLogging with AutoCloseable with NoTracing { .filter(_.config.topology.open) def toDomainConfig(domain: DomainNodeBootstrap): Either[StartupError, DomainConnectionConfig] = (for { - connection <- domain.config.sequencerConnectionConfig.toConnection + connection <- domain.config.sequencerConnectionConfig.toGrpcSequencerConnection name <- DomainAlias.create(domain.name.unwrap) sequencerConnections = SequencerConnections.single(connection) } yield DomainConnectionConfig(name, sequencerConnections)).leftMap(err => diff --git a/sdk/canton/community/app/src/pack/config/remote/domain.conf b/sdk/canton/community/app/src/pack/config/remote/domain.conf index c0bade9d1283..c342bf90863c 100644 --- a/sdk/canton/community/app/src/pack/config/remote/domain.conf +++ b/sdk/canton/community/app/src/pack/config/remote/domain.conf @@ -5,12 +5,12 @@ include required("../tls/mtls-admin-api.conf") include required("../tls/tls-public-api.conf") canton { remote-domains.mydomain { - public-api = ${?_shared.public-api-client-tls} public-api { address = localhost address = ${?REMOTE_ADDRESS} port = 10018 port = ${?PUBLIC_API_PORT} + tls = ${?_shared.public-api-client-tls} } admin-api { address = localhost diff --git a/sdk/canton/community/app/src/pack/config/remote/sequencer.conf b/sdk/canton/community/app/src/pack/config/remote/sequencer.conf index 2d5791f78f35..364c39dceb14 100644 --- a/sdk/canton/community/app/src/pack/config/remote/sequencer.conf +++ b/sdk/canton/community/app/src/pack/config/remote/sequencer.conf @@ -5,12 +5,12 @@ include required("../tls/mtls-admin-api.conf") include required("../tls/tls-public-api.conf") canton { remote-sequencers.sequencer { - public-api = ${?_shared.public-api-client-tls} public-api { address = localhost address = ${?REMOTE_ADDRESS} port = 10038 port = ${?PUBLIC_API_PORT} + tls = ${?_shared.public-api-client-tls} } admin-api { address = localhost diff --git a/sdk/canton/community/app/src/pack/config/tls/tls-public-api.conf b/sdk/canton/community/app/src/pack/config/tls/tls-public-api.conf index 1fff22e33a88..1ac971489f6d 100644 --- a/sdk/canton/community/app/src/pack/config/tls/tls-public-api.conf +++ b/sdk/canton/community/app/src/pack/config/tls/tls-public-api.conf @@ -7,8 +7,7 @@ _shared { private-key-file = ${?_TLS_CERT_LOCATION}"/public-api.pem" } public-api-client-tls { - transport-security = true - // The trust collection used to verify the server certificate. Used here because of the self-signed certs. - custom-trust-certificates.pem-file = ${?_TLS_CERT_LOCATION}"/root-ca.crt" + // The trust collection used to verify the server certificate. Used here because of the self-signed certs. + trust-collection-file = ${?_TLS_CERT_LOCATION}"/root-ca.crt" } } diff --git a/sdk/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml b/sdk/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml index 7b1d16ba6eb3..868d64e7a8f2 100644 --- a/sdk/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml +++ b/sdk/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b sandbox-options: - --wall-clock-time name: contact diff --git a/sdk/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml b/sdk/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml index 785a070a8bc5..7cc469dfaa28 100644 --- a/sdk/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml +++ b/sdk/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b sandbox-options: - --wall-clock-time name: message diff --git a/sdk/canton/community/app/src/test/resources/deprecated-configs/backwards-compatible.conf b/sdk/canton/community/app/src/test/resources/deprecated-configs/backwards-compatible.conf index 778951c6214b..9226911bda31 100644 --- a/sdk/canton/community/app/src/test/resources/deprecated-configs/backwards-compatible.conf +++ b/sdk/canton/community/app/src/test/resources/deprecated-configs/backwards-compatible.conf @@ -40,3 +40,33 @@ canton { } } } + +// deprecated 'transport-security' and 'custom-trust-certificates.pem-file' fields +// newly added 'tls.enabled' field (with default value 'true') +canton.remote-domains.domain1 { + public-api { + address = localhost + port = 10038 + transport-security = true + custom-trust-certificates.pem-file = "community/app/src/test/resources/deprecated-configs/backwards-compatible.conf" + } + admin-api { + address = localhost + port = 10039 + tls = { + trust-collection-file = "community/app/src/test/resources/deprecated-configs/backwards-compatible.conf" + } + } +} + +// Negative example for newly added 'tls.enabled' field (with default value 'true') +canton.remote-domains.domain2 { + public-api { + address = localhost + port = 10038 + } + admin-api { + address = localhost + port = 10039 + } +} diff --git a/sdk/canton/community/app/src/test/resources/deprecated-configs/new-config-fields-take-precedence.conf b/sdk/canton/community/app/src/test/resources/deprecated-configs/new-config-fields-take-precedence.conf index 9c41b93f281a..e17e474a0b1b 100644 --- a/sdk/canton/community/app/src/test/resources/deprecated-configs/new-config-fields-take-precedence.conf +++ b/sdk/canton/community/app/src/test/resources/deprecated-configs/new-config-fields-take-precedence.conf @@ -56,3 +56,33 @@ canton { domain2.domain-parameters.unique-contract-keys = false } } + +// deprecated 'transport-security' and 'custom-trust-certificates.pem-file' fields +// newly added 'tls.enabled' field (with default value 'true') +canton.remote-domains.domain1 { + public-api { + address = localhost + port = 10038 + transport-security = true + custom-trust-certificates.pem-file = "community/app/src/test/resources/deprecated-configs/new-config-fields-take-precedence.conf" + } + admin-api { + address = localhost + port = 10039 + tls = { + trust-collection-file = "community/app/src/test/resources/deprecated-configs/new-config-fields-take-precedence.conf" + } + } +} + +// Negative example for newly added 'tls.enabled' field (with default value 'true') +canton.remote-domains.domain2 { + public-api { + address = localhost + port = 10038 + } + admin-api { + address = localhost + port = 10039 + } +} diff --git a/sdk/canton/community/app/src/test/scala/com/digitalasset/canton/config/CantonCommunityConfigTest.scala b/sdk/canton/community/app/src/test/scala/com/digitalasset/canton/config/CantonCommunityConfigTest.scala index a744bc00f0d8..f4c4cb9243f0 100644 --- a/sdk/canton/community/app/src/test/scala/com/digitalasset/canton/config/CantonCommunityConfigTest.scala +++ b/sdk/canton/community/app/src/test/scala/com/digitalasset/canton/config/CantonCommunityConfigTest.scala @@ -104,6 +104,17 @@ class CantonCommunityConfigTest extends AnyWordSpec with BaseTest { domain1Parameters.uniqueContractKeys shouldBe false domain2parameters.uniqueContractKeys shouldBe true + + // test deprecated field 'custom-trust-certificates' + val remoteDomain1 = config.remoteDomains.get(InstanceName.tryCreate("domain1")).value + remoteDomain1.publicApi.tlsConfig.flatMap(_.trustCollectionFile).isDefined shouldBe true + + // test newly added tls.enabled field + remoteDomain1.adminApi.tlsConfig.isDefined shouldBe true + remoteDomain1.adminApi.tlsConfig.value.enabled shouldBe true + val remoteDomain2 = config.remoteDomains.get(InstanceName.tryCreate("domain2")).value + remoteDomain2.adminApi.tlsConfig.isDefined shouldBe false + remoteDomain2.publicApi.tlsConfig.isDefined shouldBe false } // In this test case, both deprecated and new fields are set with opposite values, we make sure the new fields diff --git a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/SequencerConnectionConfig.scala b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/SequencerConnectionConfig.scala deleted file mode 100644 index 5f0aa55294ac..000000000000 --- a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/SequencerConnectionConfig.scala +++ /dev/null @@ -1,64 +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.config - -import better.files.* -import cats.syntax.traverse.* -import com.daml.nonempty.NonEmpty -import com.digitalasset.canton.SequencerAlias -import com.digitalasset.canton.config.RequireTypes.{ExistingFile, Port} -import com.digitalasset.canton.crypto.X509CertificatePem -import com.digitalasset.canton.networking.Endpoint -import com.digitalasset.canton.sequencing.{GrpcSequencerConnection, SequencerConnection} -import com.google.protobuf.ByteString - -/** Definition provided by the domain node to members with details on how to connect to the domain sequencer. * */ -sealed trait SequencerConnectionConfig { - def toConnection: Either[String, SequencerConnection] -} - -object SequencerConnectionConfig { - - // TODO(i3804) consolidate with TlsClientCertificate - sealed trait CertificateConfig extends Product with Serializable { - def pem: X509CertificatePem - } - - /** Throws an exception if the file does not exist or cannot be loaded. */ - final case class CertificateFile(pemFile: ExistingFile) extends CertificateConfig { - override val pem: X509CertificatePem = X509CertificatePem.tryFromFile(pemFile.unwrap.toScala) - } - - /** Throws an exception if the string containing the PEM certificate cannot be loaded. */ - final case class CertificateString(pemString: String) extends CertificateConfig { - override val pem: X509CertificatePem = X509CertificatePem.tryFromString(pemString) - } - - object CertificateConfig { - def apply(bytes: ByteString): CertificateConfig = - CertificateString(bytes.toStringUtf8) - } - - /** Grpc connection using a real grpc channel. - */ - final case class Grpc( - address: String, - port: Port, - transportSecurity: Boolean = false, - customTrustCertificates: Option[CertificateFile] = None, - ) extends SequencerConnectionConfig { - - def toConnection: Either[String, GrpcSequencerConnection] = - for { - pem <- customTrustCertificates.traverse(file => - X509CertificatePem.fromFile(file.pemFile.unwrap.toScala) - ) - } yield GrpcSequencerConnection( - NonEmpty(Seq, Endpoint(address, port)), - transportSecurity, - pem.map(_.unwrap), - SequencerAlias.Default, - ) - } -} diff --git a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/ServerConfig.scala b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/ServerConfig.scala index 3ee0b475ece6..dbdacda66222 100644 --- a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/ServerConfig.scala +++ b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/config/ServerConfig.scala @@ -3,24 +3,29 @@ package com.digitalasset.canton.config +import better.files.* +import cats.syntax.traverse.* import com.daml.metrics.api.MetricHandle.MetricsFactory import com.daml.metrics.api.MetricName import com.daml.metrics.grpc.GrpcServerMetrics -import com.digitalasset.canton.ProtoDeserializationError +import com.daml.nonempty.NonEmpty import com.digitalasset.canton.config.AdminServerConfig.defaultAddress import com.digitalasset.canton.config.RequireTypes.{ExistingFile, NonNegativeInt, Port} -import com.digitalasset.canton.config.SequencerConnectionConfig.CertificateFile +import com.digitalasset.canton.crypto.X509CertificatePem import com.digitalasset.canton.domain.api.v0 import com.digitalasset.canton.ledger.api.tls.TlsVersion import com.digitalasset.canton.logging.NamedLoggerFactory import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting} +import com.digitalasset.canton.networking.Endpoint import com.digitalasset.canton.networking.grpc.{ CantonCommunityServerInterceptors, CantonServerBuilder, CantonServerInterceptors, } +import com.digitalasset.canton.sequencing.GrpcSequencerConnection import com.digitalasset.canton.serialization.ProtoConverter.ParsingResult import com.digitalasset.canton.tracing.TracingConfig +import com.digitalasset.canton.{ProtoDeserializationError, SequencerAlias} import io.netty.handler.ssl.{ClientAuth, SslContext} import org.slf4j.LoggerFactory @@ -69,13 +74,6 @@ trait ServerConfig extends Product with Serializable { /** maximum inbound message size in bytes on the ledger api and the admin api */ def maxInboundMessageSize: NonNegativeInt - def toSequencerConnectionConfig: SequencerConnectionConfig.Grpc = - SequencerConnectionConfig.Grpc( - address, - port, - serverCertChainFile.isDefined, - serverCertChainFile.map(f => CertificateFile(f)), - ) /** Use the configuration to instantiate the interceptors for this server */ def instantiateServerInterceptors( @@ -219,13 +217,52 @@ object ApiType { } } +trait GrpcClientConfig { + def address: String + def port: Port + def tlsConfig: Option[TlsClientConfig] + def toGrpcSequencerConnection: Either[String, GrpcSequencerConnection] = + for { + pem <- tlsConfig + .flatMap(_.trustCollectionFile) + .traverse(file => X509CertificatePem.fromFile(file.unwrap.toScala)) + } yield GrpcSequencerConnection( + endpoints = NonEmpty(Seq, Endpoint(address, port)), + transportSecurity = tlsConfig.exists(_.enabled), + customTrustCertificates = pem.map(_.unwrap), + sequencerAlias = SequencerAlias.Default, + ) +} + /** A client configuration to a corresponding server configuration */ final case class ClientConfig( - address: String = "127.0.0.1", - port: Port, + override val address: String = "127.0.0.1", + override val port: Port, tls: Option[TlsClientConfig] = None, keepAliveClient: Option[KeepAliveClientConfig] = Some(KeepAliveClientConfig()), -) +) extends GrpcClientConfig { + override def tlsConfig: Option[TlsClientConfig] = tls +} + +/** A sequencer API client configuration, which unlike a generic gRPC client config + * does not support TLS client certificates because the sequencer API uses auth tokens. + */ +final case class SequencerApiClientConfig( + override val address: String, + override val port: Port, + tls: Option[TlsClientConfigOnlyTrustFile] = None, +) extends GrpcClientConfig { + override def tlsConfig: Option[TlsClientConfig] = tls.map(_.toTlsClientConfig) +} + +object SequencerApiClientConfig { + def fromClientConfig(clientConfig: ClientConfig): SequencerApiClientConfig = + SequencerApiClientConfig( + clientConfig.address, + clientConfig.port, + clientConfig.tlsConfig.map(_.withoutClientCert), + ) +} sealed trait BaseTlsArguments { def certChainFile: ExistingFile @@ -371,11 +408,34 @@ final case class TlsBaseServerConfig( * * @param trustCollectionFile a file containing certificates of all nodes the client trusts. If none is specified, defaults to the JVM trust store * @param clientCert the client certificate + * @param enabled allows enabling TLS without `trustCollectionFile` or `clientCert` */ final case class TlsClientConfig( trustCollectionFile: Option[ExistingFile], clientCert: Option[TlsClientCertificate], -) + enabled: Boolean = true, +) { + def withoutClientCert: TlsClientConfigOnlyTrustFile = TlsClientConfigOnlyTrustFile( + trustCollectionFile = trustCollectionFile, + enabled = enabled, + ) +} + +/** A wrapper for TLS related client configurations without client auth support (currently public sequencer api) + * + * @param trustCollectionFile a file containing certificates of all nodes the client trusts. If none is specified, defaults to the JVM trust store + * @param enabled allows enabling TLS without `trustCollecionFile` or `clientCert` + */ +final case class TlsClientConfigOnlyTrustFile( + trustCollectionFile: Option[ExistingFile], + enabled: Boolean = true, +) { + def toTlsClientConfig: TlsClientConfig = TlsClientConfig( + trustCollectionFile = trustCollectionFile, + clientCert = None, + enabled = enabled, + ) +} /** */ diff --git a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/networking/grpc/ClientChannelBuilder.scala b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/networking/grpc/ClientChannelBuilder.scala index b085c5d061af..05dea0c7dc1f 100644 --- a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/networking/grpc/ClientChannelBuilder.scala +++ b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/networking/grpc/ClientChannelBuilder.scala @@ -152,7 +152,7 @@ object ClientChannelBuilder { configureKeepAlive( clientConfig.keepAliveClient, // if tls isn't configured assume that it's a plaintext channel - clientConfig.tls + clientConfig.tlsConfig .fold(baseBuilder.usePlaintext()) { tls => baseBuilder .useTransportSecurity() diff --git a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/sequencing/SequencerConnection.scala b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/sequencing/SequencerConnection.scala index 266c5bdfefe0..c4502a96fbf4 100644 --- a/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/sequencing/SequencerConnection.scala +++ b/sdk/canton/community/base/src/main/scala/com/digitalasset/canton/sequencing/SequencerConnection.scala @@ -19,7 +19,7 @@ import io.grpc.netty.NettyChannelBuilder import java.net.URI import java.util.concurrent.Executor -/** Our [[com.digitalasset.canton.config.SequencerConnectionConfig]] provides a flexible structure for configuring how +/** This provides a flexible structure for configuring how * the domain and its members talk to a sequencer. It however leaves much information intentionally optional so it can * be inferred at runtime based on information that may only be available at the point of creating a sequencer * connection (for instance defaulting to domain connection information that a user has provided in an admin command). diff --git a/sdk/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/codegen/ContractWithKey.java b/sdk/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/codegen/ContractWithKey.java index 8707eed610f9..d839cde6d2fd 100644 --- a/sdk/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/codegen/ContractWithKey.java +++ b/sdk/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/codegen/ContractWithKey.java @@ -64,12 +64,7 @@ public final String toString() { this.observers); } - // Returns an encoder for the key if present, or null otherwise. - // TODO(raphael-speyer-da): Make abstract once https://github.com/digital-asset/daml/pull/18198 - // goes through and all generated classes do in fact define this method. - public JsonLfEncoder keyJsonEncoder() { - return null; - } + public abstract JsonLfEncoder keyJsonEncoder(); public String keyToJson() { JsonLfEncoder enc = keyJsonEncoder(); diff --git a/sdk/canton/community/common/src/main/daml/CantonExamples/daml.yaml b/sdk/canton/community/common/src/main/daml/CantonExamples/daml.yaml index e4c075bd459b..976100569838 100644 --- a/sdk/canton/community/common/src/main/daml/CantonExamples/daml.yaml +++ b/sdk/canton/community/common/src/main/daml/CantonExamples/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b build-options: - --target=1.14 name: CantonExamples diff --git a/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/V999__dev.sha256 b/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/V999__dev.sha256 index 168066a33cb8..fb58e070cdd7 100644 --- a/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/V999__dev.sha256 +++ b/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/V999__dev.sha256 @@ -1 +1 @@ -01a887c4f5b6915891873cace8d8e1cbb1b1bf19fae807235998064b3befb558 +8ecaf2eede5b0d860d0372fe01ab3f328564e095c52763504332e8c665f0262b diff --git a/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/reference/V998__blocks.sha256 b/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/reference/V998__blocks.sha256 new file mode 100644 index 000000000000..f2496a2afe19 --- /dev/null +++ b/sdk/canton/community/common/src/main/resources/db/migration/canton/h2/dev/reference/V998__blocks.sha256 @@ -0,0 +1 @@ +d819cf762d646371351ba41d8ad450e3938b4467f70241a01c09ae13053c9173 diff --git a/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/V999__dev.sha256 b/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/V999__dev.sha256 index 79a29c0c9947..fb58e070cdd7 100644 --- a/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/V999__dev.sha256 +++ b/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/V999__dev.sha256 @@ -1 +1 @@ -332e31af4d57c1ad45268a448cb376a8eaf3a5a2d7c80c46136878438907885c +8ecaf2eede5b0d860d0372fe01ab3f328564e095c52763504332e8c665f0262b diff --git a/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/reference/V998__blocks.sha256 b/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/reference/V998__blocks.sha256 new file mode 100644 index 000000000000..4b05077b6103 --- /dev/null +++ b/sdk/canton/community/common/src/main/resources/db/migration/canton/postgres/dev/reference/V998__blocks.sha256 @@ -0,0 +1 @@ +61d2a906703a43f7073fb263161282f27293f8ce4e8b7b874429f6a6b53f0a13 diff --git a/sdk/canton/community/common/src/main/scala/com/digitalasset/canton/config/DeprecatedConfigUtils.scala b/sdk/canton/community/common/src/main/scala/com/digitalasset/canton/config/DeprecatedConfigUtils.scala index 29c409b74774..3de96bebacfa 100644 --- a/sdk/canton/community/common/src/main/scala/com/digitalasset/canton/config/DeprecatedConfigUtils.scala +++ b/sdk/canton/community/common/src/main/scala/com/digitalasset/canton/config/DeprecatedConfigUtils.scala @@ -22,6 +22,7 @@ object DeprecatedConfigUtils { path: String, since: String, valueFilter: Option[T] = None, + dropPath: Boolean = false, ) { def isDeprecatedValue(configValue: ConfigValue): Boolean = valueFilter.forall { dValue => @@ -127,7 +128,17 @@ object DeprecatedConfigUtils { .getOrElse(" ")}is deprecated since ${deprecated.since}" ) } - } yield ConfigCursor(cursorConfigValue, cursor.pathElems) + adjustedConfig = + if (deprecated.dropPath) { + ConfigFactory + .empty() + .withFallback(cursorConfigValue) + .withoutPath(deprecated.path) + .root() + } else { + cursorConfigValue + } + } yield ConfigCursor(adjustedConfig, cursor.pathElems) result.getOrElse(cursor) } diff --git a/sdk/canton/community/demo/src/main/daml/ai-analysis/daml.yaml b/sdk/canton/community/demo/src/main/daml/ai-analysis/daml.yaml index 078711b39820..353bece87633 100644 --- a/sdk/canton/community/demo/src/main/daml/ai-analysis/daml.yaml +++ b/sdk/canton/community/demo/src/main/daml/ai-analysis/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b name: ai-analysis source: AIAnalysis.daml init-script: AIAnalysis:setup diff --git a/sdk/canton/community/demo/src/main/daml/bank/daml.yaml b/sdk/canton/community/demo/src/main/daml/bank/daml.yaml index 580e24391270..5320c8ef18b2 100644 --- a/sdk/canton/community/demo/src/main/daml/bank/daml.yaml +++ b/sdk/canton/community/demo/src/main/daml/bank/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b name: bank source: Bank.daml init-script: Bank:setup diff --git a/sdk/canton/community/demo/src/main/daml/doctor/daml.yaml b/sdk/canton/community/demo/src/main/daml/doctor/daml.yaml index 6692c4707d11..c832f6147e3b 100644 --- a/sdk/canton/community/demo/src/main/daml/doctor/daml.yaml +++ b/sdk/canton/community/demo/src/main/daml/doctor/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b name: doctor source: Doctor.daml init-script: Doctor:setup diff --git a/sdk/canton/community/demo/src/main/daml/health-insurance/daml.yaml b/sdk/canton/community/demo/src/main/daml/health-insurance/daml.yaml index 5e40003a083e..08da0fbe1050 100644 --- a/sdk/canton/community/demo/src/main/daml/health-insurance/daml.yaml +++ b/sdk/canton/community/demo/src/main/daml/health-insurance/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b name: health-insurance source: HealthInsurance.daml init-script: HealthInsurance:setup diff --git a/sdk/canton/community/demo/src/main/daml/medical-records/daml.yaml b/sdk/canton/community/demo/src/main/daml/medical-records/daml.yaml index ee5e841899f1..39b090ad562a 100644 --- a/sdk/canton/community/demo/src/main/daml/medical-records/daml.yaml +++ b/sdk/canton/community/demo/src/main/daml/medical-records/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b name: medical-records source: MedicalRecord.daml init-script: MedicalRecord:setup diff --git a/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/DomainNodeBootstrap.scala b/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/DomainNodeBootstrap.scala index 483ddf18b773..7e0a7a077bda 100644 --- a/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/DomainNodeBootstrap.scala +++ b/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/DomainNodeBootstrap.scala @@ -450,7 +450,7 @@ class DomainNodeBootstrap( val sequencedTopologyStore = sequencerTopologyStore.initOrGet(DomainStore(domainId)) val publicSequencerConnectionEitherT = - config.publicApi.toSequencerConnectionConfig.toConnection.toEitherT[Future] + config.publicApi.clientConfig.toGrpcSequencerConnection.toEitherT[Future] for { publicSequencerConnection <- publicSequencerConnectionEitherT diff --git a/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/config/DomainConfig.scala b/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/config/DomainConfig.scala index c1eb22f15260..76d56b2561f5 100644 --- a/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/config/DomainConfig.scala +++ b/sdk/canton/community/domain/src/main/scala/com/digitalasset/canton/domain/config/DomainConfig.scala @@ -141,12 +141,12 @@ trait DomainConfig extends DomainBaseConfig { /** location of the service agreement of the domain (if any) */ def serviceAgreement: Option[File] - def sequencerConnectionConfig: SequencerConnectionConfig.Grpc = - publicApi.toSequencerConnectionConfig + def sequencerConnectionConfig: ClientConfig = + publicApi.clientConfig def toRemoteConfig: RemoteDomainConfig = RemoteDomainConfig( adminApi = adminApi.clientConfig, - publicApi = sequencerConnectionConfig, + publicApi = SequencerApiClientConfig.fromClientConfig(sequencerConnectionConfig), ) /** General node parameters */ @@ -199,7 +199,7 @@ final case class CommunityDomainConfig( */ final case class RemoteDomainConfig( adminApi: ClientConfig, - publicApi: SequencerConnectionConfig.Grpc, + publicApi: SequencerApiClientConfig, ) extends NodeConfig { override def clientAdminApi: ClientConfig = adminApi } diff --git a/sdk/canton/community/ledger/ledger-api-core/src/main/scala/com/digitalasset/canton/platform/store/backend/postgresql/PostgresContractStorageBackend.scala b/sdk/canton/community/ledger/ledger-api-core/src/main/scala/com/digitalasset/canton/platform/store/backend/postgresql/PostgresContractStorageBackend.scala index 065f53bfa313..546e256fd73b 100644 --- a/sdk/canton/community/ledger/ledger-api-core/src/main/scala/com/digitalasset/canton/platform/store/backend/postgresql/PostgresContractStorageBackend.scala +++ b/sdk/canton/community/ledger/ledger-api-core/src/main/scala/com/digitalasset/canton/platform/store/backend/postgresql/PostgresContractStorageBackend.scala @@ -37,7 +37,6 @@ class PostgresContractStorageBackend( hash -> KeyAssigned(cId, stakeholders.view.map(stringInterning.party.externalize).toSet) }.* - import com.digitalasset.canton.platform.store.backend.Conversions.HashToStatement import com.digitalasset.canton.platform.store.backend.Conversions.OffsetToStatement // efficient adaption of the "single lookup query" using postgres specific syntax @@ -49,7 +48,7 @@ class PostgresContractStorageBackend( .SQL(s""" WITH last_contract_key_create AS ( SELECT p.contract_id, p.create_key_hash, p.flat_event_witnesses - FROM UNNEST(ARRAY[{keys}]) AS k(create_key_hash) + FROM UNNEST({keys}) AS k(create_key_hash) CROSS JOIN LATERAL ( SELECT * FROM participant_events_create p @@ -67,7 +66,10 @@ class PostgresContractStorageBackend( WHERE contract_id = last_contract_key_create.contract_id AND event_offset <= {validAt} )""") - .on("keys" -> keys.map(_.hash), "validAt" -> validAt) + .on( + "keys" -> keys.view.map(_.hash.bytes.toHexString.toString).toArray, + "validAt" -> validAt, + ) .as(resultParser)(connection) .toMap keys.map { case (key) => diff --git a/sdk/canton/community/ledger/ledger-json-api/src/test/daml/daml.yaml b/sdk/canton/community/ledger/ledger-json-api/src/test/daml/daml.yaml index 8256f990b3c0..38da78c6bda8 100644 --- a/sdk/canton/community/ledger/ledger-json-api/src/test/daml/daml.yaml +++ b/sdk/canton/community/ledger/ledger-json-api/src/test/daml/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b build-options: - --target=1.14 name: JsonEncodingTest diff --git a/sdk/canton/community/participant/src/main/daml/daml.yaml b/sdk/canton/community/participant/src/main/daml/daml.yaml index 0f92a22ffc46..532152e6b0c2 100644 --- a/sdk/canton/community/participant/src/main/daml/daml.yaml +++ b/sdk/canton/community/participant/src/main/daml/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b name: AdminWorkflows source: AdminWorkflows.daml version: 2.10.0 diff --git a/sdk/canton/community/participant/src/main/daml/ping-pong-vacuum/daml.yaml b/sdk/canton/community/participant/src/main/daml/ping-pong-vacuum/daml.yaml index 8c21ab4284b2..1351cb428a46 100644 --- a/sdk/canton/community/participant/src/main/daml/ping-pong-vacuum/daml.yaml +++ b/sdk/canton/community/participant/src/main/daml/ping-pong-vacuum/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.10.0-snapshot.20241216.13150.0.vc832cb4a +sdk-version: 2.10.0-snapshot.20241217.13156.0.v1da3a35b build-options: - --target=1.14 name: AdminWorkflowsWithVacuuming diff --git a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ledger/api/client/LedgerConnection.scala b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ledger/api/client/LedgerConnection.scala index eccce22f1402..475241b601a4 100644 --- a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ledger/api/client/LedgerConnection.scala +++ b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ledger/api/client/LedgerConnection.scala @@ -145,7 +145,7 @@ object LedgerConnection { token = token, ) val clientChannelConfig = LedgerClientChannelConfiguration( - sslContext = config.tls.map(x => ClientChannelBuilder.sslContext(x)), + sslContext = config.tlsConfig.map(x => ClientChannelBuilder.sslContext(x)), // Hard-coding the maximum value (= 2GB). // If a limit is needed, because an application can't handle transactions at that size, // the participants should agree on a lower limit and enforce that through domain parameters. diff --git a/sdk/test-common/canton/BUILD.bazel b/sdk/test-common/canton/BUILD.bazel index e90131ebd1d2..b151b6e3e678 100644 --- a/sdk/test-common/canton/BUILD.bazel +++ b/sdk/test-common/canton/BUILD.bazel @@ -24,9 +24,9 @@ if [ "{local}" = "true" ]; then exit 0 fi -CANTON_ENTERPRISE_VERSION=2.10.0-snapshot.20241217.12411.0.vac57160f -CANTON_ENTERPRISE_SHA=739498e98cd00737beed282b171e42f45a76b8e2f0bfa02048196efd94efff8f -CANTON_ENTERPRISE_URL=https://digitalasset.jfrog.io/artifactory/assembly/daml/canton-backup/2.10.0-snapshot.20241217.12411.0.vac57160f/739498e98cd00737beed282b171e42f45a76b8e2f0bfa02048196efd94efff8f/canton-enterprise-2.10.0-snapshot.20241217.12411.0.vac57160f.tar.gz +CANTON_ENTERPRISE_VERSION=2.10.0-snapshot.20241220.12422.0.v3b033b98 +CANTON_ENTERPRISE_SHA=221b5bfcdafcff62134c122e8a35d66f1679cf9ef826d40473649e3e41e30ade +CANTON_ENTERPRISE_URL=https://digitalasset.jfrog.io/artifactory/assembly/daml/canton-backup/2.10.0-snapshot.20241220.12422.0.v3b033b98/221b5bfcdafcff62134c122e8a35d66f1679cf9ef826d40473649e3e41e30ade/canton-enterprise-2.10.0-snapshot.20241220.12422.0.v3b033b98.tar.gz url=$$CANTON_ENTERPRISE_URL