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 12a62246502e..cec3d8aee8c2 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 @@ -396,6 +396,7 @@ trait CantonConfig { // TODO(i21341) Remove the flag before going to production experimentalEnableTopologyEvents = participantParameters.experimentalEnableTopologyEvents, enableExternalAuthorization = participantParameters.enableExternalAuthorization, + allowDamlScriptUpload = participantParameters.allowDamlScriptUpload, ) } diff --git a/sdk/canton/community/ledger/ledger-common/src/main/scala/com/digitalasset/canton/ledger/error/PackageServiceErrors.scala b/sdk/canton/community/ledger/ledger-common/src/main/scala/com/digitalasset/canton/ledger/error/PackageServiceErrors.scala index 9e088471fcac..8029510beb47 100644 --- a/sdk/canton/community/ledger/ledger-common/src/main/scala/com/digitalasset/canton/ledger/error/PackageServiceErrors.scala +++ b/sdk/canton/community/ledger/ledger-common/src/main/scala/com/digitalasset/canton/ledger/error/PackageServiceErrors.scala @@ -330,5 +330,37 @@ object PackageServiceErrors extends PackageServiceErrorGroup { ), ) } + + @Explanation( + """Upload of Daml-Script is heavily deprecated in Daml 3.2, and will be forbidden in Daml 3.3""" + ) + @Resolution( + """Remove Daml-Script from the dependencies of the uploaded package. + |For Daml 3.2 only, `parameters.allow-daml-script-upload = true` can be added to your participant config + |""".stripMargin + ) + object IllegalDamlScriptUpload + extends ErrorCode( + id = "ILLEGAL_DAML_SCRIPT_UPLOAD", + ErrorCategory.InvalidIndependentOfSystemState, + ) { + final case class Error( + packages: List[(Ref.PackageId, Ref.PackageName, Ref.PackageVersion)] + )(implicit + val loggingContext: ContextualizedErrorLogger + ) extends DamlError( + cause = { + val packagesStr = packages.map{case (pkgId, pkgName, pkgVersion) => s" - $pkgId ($pkgName-$pkgVersion)\n"} + s"Illegal upload of daml-script package(s)\n$packagesStr" + + "Uploading of daml-script to canton is deprecated in Daml 3.2, and will be disallowed in 3.3\n" + + "Please remove daml-script from the dependencies of the uploaded dar\n" + + "To temporarily avoid this error, add `parameters.allow-daml-script-upload = true` to your participant config, but be aware that this parameter will not exist in Daml 3.3" + }, + extraContext = Map( + "packageIds" -> packages.map(_._1), + + ), + ) + } } } diff --git a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNode.scala b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNode.scala index 3baa762d95f7..ee265d1be310 100644 --- a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNode.scala +++ b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNode.scala @@ -534,10 +534,12 @@ class ParticipantNodeBootstrap( packageMetadataViewConfig = config.parameters.packageMetadataView, packageOps = createPackageOps(syncDomainPersistentStateManager), timeouts = parameterConfig.processingTimeouts, + allowDamlScriptUpload = parameterConfig.allowDamlScriptUpload, ), loggerFactory = loggerFactory, ) _ <- EitherT.right(packageServiceContainer.initializeNext()) + _ <- packageServiceContainer.asEval.value.checkForDamlScript() sequencerInfoLoader = new SequencerInfoLoader( parameterConfig.processingTimeouts, diff --git a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNodeParameters.scala b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNodeParameters.scala index 96ccb2829545..0dd026881652 100644 --- a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNodeParameters.scala +++ b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/ParticipantNodeParameters.scala @@ -31,6 +31,7 @@ final case class ParticipantNodeParameters( unsafeEnableOnlinePartyReplication: Boolean, experimentalEnableTopologyEvents: Boolean, enableExternalAuthorization: Boolean, + allowDamlScriptUpload: Boolean, ) extends CantonNodeParameters with HasGeneralCantonNodeParameters { override def dontWarnOnDeprecatedPV: Boolean = protocolConfig.dontWarnOnDeprecatedPV @@ -81,5 +82,6 @@ object ParticipantNodeParameters { unsafeEnableOnlinePartyReplication = false, experimentalEnableTopologyEvents = true, enableExternalAuthorization = false, + allowDamlScriptUpload = false, ) } diff --git a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageService.scala b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageService.scala index 0998433589f4..653dc5e4213d 100644 --- a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageService.scala +++ b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageService.scala @@ -8,6 +8,7 @@ import cats.implicits.toBifunctorOps import cats.syntax.functor.* import cats.syntax.functorFilter.* import cats.syntax.parallel.* +import cats.syntax.traverse.* import com.daml.error.{ContextualizedErrorLogger, DamlError} import com.digitalasset.canton.LedgerSubmissionId import com.digitalasset.canton.concurrent.FutureSupervisor @@ -81,6 +82,7 @@ class PackageService( packageOps: PackageOps, packageUploader: PackageUploader, protected val timeouts: ProcessingTimeout, + val allowDamlScriptUpload: Boolean, )(implicit ec: ExecutionContext) extends DarService with PackageInfoService @@ -366,7 +368,31 @@ class PackageService( override def onClosed(): Unit = Lifecycle.close(packageUploader, packageMetadataView)(logger) -} + def checkForDamlScript()(implicit traceContext: TraceContext): EitherT[FutureUnlessShutdown, String, Unit] = + if (!allowDamlScriptUpload) { + val snapshot = packageMetadataView.getSnapshot + val illegalPackages = snapshot.packageNameMap.toList.flatMap{ + case (name @ ("daml-script" | "daml3-script" | "daml-script-lts" | "daml-script-lts-stable"), res) => + res.allPackageIdsForName.toList.map(pkgId => (pkgId, name)) + case _ => List.empty + } + + if (!illegalPackages.isEmpty) { + illegalPackages.traverse{ + case (pkgId, pkgName) => EitherT.liftF(getDescription(pkgId).map(desc => (pkgId, pkgName, desc.map(_.sourceDescription.str)))).mapK(FutureUnlessShutdown.outcomeK) + }.flatMap{pkgs => + val pkgsStr = pkgs.map{case (pkgId, pkgName, pkgSource) => s" - $pkgId ($pkgName)" + pkgSource.fold("\n")(src => s" (from dar: $src)\n")}.mkString + val err = + "\nFound illegal daml-script package(s) in package store:\n" + + pkgsStr + + "Uploading of daml-script to canton is deprecated in Daml 3.2, and will be disallowed in 3.3\n" + + "Please remove daml-script from dependencies of all uploaded packages\n" + + "To temporarily avoid this error, add `parameters.allow-daml-script-upload = true` to your participant config, but be aware that this parameter will not exist in Daml 3.3" + EitherT.leftT[FutureUnlessShutdown, Unit](err) + } + } else EitherT.rightT[FutureUnlessShutdown, String](()) + } else EitherT.rightT[FutureUnlessShutdown, String](()) + } object PackageService { def createAndInitialize( @@ -382,6 +408,7 @@ object PackageService { packageMetadataViewConfig: PackageMetadataViewConfig, packageOps: PackageOps, timeouts: ProcessingTimeout, + allowDamlScriptUpload: Boolean, )(implicit ec: ExecutionContext, actorSystem: ActorSystem, @@ -404,6 +431,7 @@ object PackageService { packageDependencyResolver, mutablePackageMetadataView, exitOnFatalFailures = exitOnFatalFailures, + allowDamlScriptUpload, timeouts, loggerFactory, ) @@ -416,6 +444,7 @@ object PackageService { packageOps, packageUploader, timeouts, + allowDamlScriptUpload, ) // Initialize the packageMetadataView and return only the PackageService. It also takes care of teardown of the packageMetadataView and packageUploader diff --git a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageUploader.scala b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageUploader.scala index 135f57b64e48..d42254657510 100644 --- a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageUploader.scala +++ b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/admin/PackageUploader.scala @@ -52,6 +52,7 @@ class PackageUploader( packageDependencyResolver: PackageDependencyResolver, packageMetadataView: MutablePackageMetadataView, exitOnFatalFailures: Boolean, + allowDamlScriptUpload: Boolean, protected val timeouts: ProcessingTimeout, protected val loggerFactory: NamedLoggerFactory, )(implicit executionContext: ExecutionContext) @@ -219,6 +220,18 @@ class PackageUploader( PackageServiceErrors.Validation.handleLfEnginePackageError(_): DamlError ) ) + _ <- + EitherT.fromEither[FutureUnlessShutdown]{ + if (!allowDamlScriptUpload) { + val illegalPackages = packages.collect{ + case (pkgId, pkg) if List("daml-script", "daml3-script", "daml-script-lts", "daml-script-lts-stable").contains(pkg.metadata.name) => + (pkgId, pkg.metadata.name, pkg.metadata.version) + } + if (!illegalPackages.isEmpty) Left(PackageServiceErrors.Validation.IllegalDamlScriptUpload.Error(illegalPackages)) + else Right(()) + } else Right(()) + } + _ <- if (enableUpgradeValidation) { packageUpgradeValidator diff --git a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/config/LocalParticipantConfig.scala b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/config/LocalParticipantConfig.scala index 59fa2798cb64..f41f2cf40a31 100644 --- a/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/config/LocalParticipantConfig.scala +++ b/sdk/canton/community/participant/src/main/scala/com/digitalasset/canton/participant/config/LocalParticipantConfig.scala @@ -274,6 +274,7 @@ object TestingTimeServiceConfig { * @param packageMetadataView Initialization parameters for the package metadata in-memory store. * @param experimentalEnableTopologyEvents If true, topology events are propagated to the Ledger API clients * @param enableExternalAuthorization If true, external authentication is supported + * @param allowDamlScriptUpload Deprecated: Allow daml-script to be uploaded to the ledger. This will be removed in Daml 3.3. */ final case class ParticipantNodeParameterConfig( adminWorkflow: AdminWorkflowConfig = AdminWorkflowConfig(), @@ -307,6 +308,7 @@ final case class ParticipantNodeParameterConfig( unsafeEnableOnlinePartyReplication: Boolean = false, experimentalEnableTopologyEvents: Boolean = false, enableExternalAuthorization: Boolean = false, + allowDamlScriptUpload: Boolean = false, ) extends LocalNodeParametersConfig /** Parameters for the participant node's stores diff --git a/sdk/canton/community/participant/src/test/scala/com/digitalasset/canton/participant/protocol/reassignment/DAMLeTestInstance.scala b/sdk/canton/community/participant/src/test/scala/com/digitalasset/canton/participant/protocol/reassignment/DAMLeTestInstance.scala index 77e81396723b..a1ef1e3e1f07 100644 --- a/sdk/canton/community/participant/src/test/scala/com/digitalasset/canton/participant/protocol/reassignment/DAMLeTestInstance.scala +++ b/sdk/canton/community/participant/src/test/scala/com/digitalasset/canton/participant/protocol/reassignment/DAMLeTestInstance.scala @@ -52,6 +52,7 @@ object DAMLeTestInstance { enableUpgradeValidation = false, futureSupervisor = FutureSupervisor.Noop, exitOnFatalFailures = true, + allowDamlScriptUpload = false, timeouts = timeouts, loggerFactory = loggerFactory, packageMetadataView = NoopPackageMetadataView,