[ED] Enable explicit disclosure in conformance tests [DPP-1206] (#14974)

* Allow enabling explicit disclosure in Sandbox-on-X

changelog_begin
changelog_end

* Fix conformance tests

* Disable tests testing yet unsupported features

* Addressed Martino's review comments
This commit is contained in:
tudor-da 2022-09-23 09:55:19 +02:00 committed by GitHub
parent dd5543eaf2
commit e0dc3f8679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 242 additions and 48 deletions

View File

@ -278,6 +278,10 @@ final class CommandsValidator(
} }
object CommandsValidator { object CommandsValidator {
def apply(ledgerId: LedgerId, explicitDisclosureUnsafeEnabled: Boolean) = new CommandsValidator(
ledgerId = ledgerId,
validateDisclosedContracts = new ValidateDisclosedContracts(explicitDisclosureUnsafeEnabled),
)
/** Effective submitters of a command /** Effective submitters of a command
* @param actAs Guaranteed to be non-empty. Will contain exactly one element in most cases. * @param actAs Guaranteed to be non-empty. Will contain exactly one element in most cases.

View File

@ -26,6 +26,7 @@ class GrpcCommandService(
currentUtcTime: () => Instant, currentUtcTime: () => Instant,
maxDeduplicationDuration: () => Option[Duration], maxDeduplicationDuration: () => Option[Duration],
generateSubmissionId: SubmissionIdGenerator, generateSubmissionId: SubmissionIdGenerator,
explicitDisclosureUnsafeEnabled: Boolean,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext) )(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends CommandService extends CommandService
with GrpcApiService with GrpcApiService
@ -34,7 +35,7 @@ class GrpcCommandService(
protected implicit val logger: ContextualizedLogger = ContextualizedLogger.get(getClass) protected implicit val logger: ContextualizedLogger = ContextualizedLogger.get(getClass)
private[this] val validator = new SubmitAndWaitRequestValidator( private[this] val validator = new SubmitAndWaitRequestValidator(
new CommandsValidator(ledgerId) CommandsValidator(ledgerId, explicitDisclosureUnsafeEnabled)
) )
override def submitAndWait(request: SubmitAndWaitRequest): Future[Empty] = override def submitAndWait(request: SubmitAndWaitRequest): Future[Empty] =

View File

@ -34,6 +34,7 @@ class GrpcCommandSubmissionService(
maxDeduplicationDuration: () => Option[Duration], maxDeduplicationDuration: () => Option[Duration],
submissionIdGenerator: SubmissionIdGenerator, submissionIdGenerator: SubmissionIdGenerator,
metrics: Metrics, metrics: Metrics,
explicitDisclosureUnsafeEnabled: Boolean,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext) )(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends ApiCommandSubmissionService extends ApiCommandSubmissionService
with ProxyCloseable with ProxyCloseable
@ -41,7 +42,7 @@ class GrpcCommandSubmissionService(
protected implicit val logger: ContextualizedLogger = ContextualizedLogger.get(getClass) protected implicit val logger: ContextualizedLogger = ContextualizedLogger.get(getClass)
private val validator = new SubmitRequestValidator( private val validator = new SubmitRequestValidator(
new CommandsValidator(ledgerId) CommandsValidator(ledgerId, explicitDisclosureUnsafeEnabled)
) )
override def submit(request: ApiSubmitRequest): Future[Empty] = { override def submit(request: ApiSubmitRequest): Future[Empty] = {

View File

@ -12,18 +12,20 @@ import com.daml.ledger.api.v1.command_service.{
SubmitAndWaitForTransactionTreeResponse, SubmitAndWaitForTransactionTreeResponse,
SubmitAndWaitRequest, SubmitAndWaitRequest,
} }
import com.daml.ledger.api.v1.commands.{Command, CreateCommand} import com.daml.ledger.api.v1.commands.{Command, CreateCommand, DisclosedContract}
import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value} import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value}
import com.daml.lf.data.Ref import com.daml.lf.data.Ref
import com.daml.logging.LoggingContext import com.daml.logging.LoggingContext
import com.google.protobuf.empty.Empty import com.google.protobuf.empty.Empty
import org.mockito.{ArgumentMatchersSugar, MockitoSugar} import org.mockito.{ArgumentMatchersSugar, MockitoSugar}
import org.scalatest.Assertion
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AsyncWordSpec import org.scalatest.wordspec.AsyncWordSpec
import java.time.{Duration, Instant} import java.time.{Duration, Instant}
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import scala.concurrent.Future import scala.concurrent.Future
import scala.util.{Failure, Success}
class GrpcCommandServiceSpec class GrpcCommandServiceSpec
extends AsyncWordSpec extends AsyncWordSpec
@ -59,6 +61,7 @@ class GrpcCommandServiceSpec
Ref.SubmissionId.assertFromString( Ref.SubmissionId.assertFromString(
s"$submissionIdPrefix${submissionCounter.incrementAndGet()}" s"$submissionIdPrefix${submissionCounter.incrementAndGet()}"
), ),
explicitDisclosureUnsafeEnabled = false,
) )
for { for {
@ -85,6 +88,51 @@ class GrpcCommandServiceSpec
succeed succeed
} }
} }
"reject submission on explicit disclosure disabled with provided disclosed contracts" in {
val mockCommandService = mock[CommandService with AutoCloseable]
val grpcCommandService = new GrpcCommandService(
mockCommandService,
ledgerId = LedgerId(ledgerId),
currentLedgerTime = () => Instant.EPOCH,
currentUtcTime = () => Instant.EPOCH,
maxDeduplicationDuration = () => Some(Duration.ZERO),
generateSubmissionId = () => Ref.SubmissionId.assertFromString(s"submissionId"),
explicitDisclosureUnsafeEnabled = false,
)
val submissionWithDisclosedContracts = aSubmitAndWaitRequestWithNoSubmissionId.update(
_.commands.disclosedContracts.set(Seq(DisclosedContract()))
)
def expectFailedOnProvidedDisclosedContracts(f: Future[_]): Future[Assertion] = f.transform {
case Failure(exception)
if exception.getMessage.contains(
"feature in development: disclosed_contracts should not be set"
) =>
Success(succeed)
case other => fail(s"Unexpected result: $other")
}
for {
_ <- expectFailedOnProvidedDisclosedContracts(
grpcCommandService.submitAndWait(submissionWithDisclosedContracts)
)
_ <- expectFailedOnProvidedDisclosedContracts(
grpcCommandService.submitAndWaitForTransaction(submissionWithDisclosedContracts)
)
_ <- expectFailedOnProvidedDisclosedContracts(
grpcCommandService.submitAndWaitForTransactionId(submissionWithDisclosedContracts)
)
_ <- expectFailedOnProvidedDisclosedContracts(
grpcCommandService.submitAndWaitForTransactionTree(submissionWithDisclosedContracts)
)
} yield {
verifyZeroInteractions(mockCommandService)
succeed
}
}
} }
} }

View File

@ -8,7 +8,7 @@ import com.codahale.metrics.MetricRegistry
import com.daml.ledger.api.domain.LedgerId import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.messages.command.submission.SubmitRequest import com.daml.ledger.api.messages.command.submission.SubmitRequest
import com.daml.ledger.api.testing.utils.MockMessages._ import com.daml.ledger.api.testing.utils.MockMessages._
import com.daml.ledger.api.v1.commands.{Command, CreateCommand} import com.daml.ledger.api.v1.commands.{Command, CreateCommand, DisclosedContract}
import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value} import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value}
import com.daml.lf.data.Ref import com.daml.lf.data.Ref
import com.daml.logging.LoggingContext import com.daml.logging.LoggingContext
@ -21,6 +21,7 @@ import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AsyncWordSpec import org.scalatest.wordspec.AsyncWordSpec
import scala.concurrent.Future import scala.concurrent.Future
import scala.util.{Failure, Success}
class GrpcCommandSubmissionServiceSpec class GrpcCommandSubmissionServiceSpec
extends AsyncWordSpec extends AsyncWordSpec
@ -87,6 +88,28 @@ class GrpcCommandSubmissionServiceSpec
requestCaptor.value.commands.submissionId shouldBe Some(generatedSubmissionId) requestCaptor.value.commands.submissionId shouldBe Some(generatedSubmissionId)
} }
} }
"reject submission on explicit disclosure disabled with provided disclosed contracts" in {
val mockCommandSubmissionService = mock[CommandSubmissionService with AutoCloseable]
val submissionWithDisclosedContracts =
aSubmitRequest.update(_.commands.disclosedContracts.set(Seq(DisclosedContract())))
grpcCommandSubmissionService(mockCommandSubmissionService)
.submit(submissionWithDisclosedContracts)
.map { _ =>
verifyZeroInteractions(mockCommandSubmissionService)
succeed
}
.transform {
case Failure(exception)
if exception.getMessage.contains(
"feature in development: disclosed_contracts should not be set"
) =>
Success(succeed)
case other => fail(s"Unexpected result: $other")
}
}
} }
private def grpcCommandSubmissionService( private def grpcCommandSubmissionService(
@ -100,6 +123,7 @@ class GrpcCommandSubmissionServiceSpec
maxDeduplicationDuration = () => Some(Duration.ZERO), maxDeduplicationDuration = () => Some(Duration.ZERO),
submissionIdGenerator = () => Ref.SubmissionId.assertFromString(generatedSubmissionId), submissionIdGenerator = () => Ref.SubmissionId.assertFromString(generatedSubmissionId),
metrics = new Metrics(new MetricRegistry), metrics = new Metrics(new MetricRegistry),
explicitDisclosureUnsafeEnabled = false,
) )
} }

View File

@ -4,6 +4,7 @@
package com.daml.ledger.api.testtool.suites.v1_dev package com.daml.ledger.api.testtool.suites.v1_dev
import com.daml.error.definitions.LedgerApiErrors import com.daml.error.definitions.LedgerApiErrors
import com.daml.ledger.api.refinements.ApiTypes.Party
import com.daml.ledger.api.testtool.infrastructure.Allocation._ import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions._ import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
@ -100,7 +101,7 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
// Create contract with `owner` as only stakeholder // Create contract with `owner` as only stakeholder
_ <- ledger.create(owner, WithKey(owner)) _ <- ledger.create(owner, WithKey(owner))
withKeyTxIds <- ledger.flatTransactionsByTemplateId(WithKey.id, owner) withKeyTxIds <- ledger.flatTransactionsByTemplateId(WithKey.id, owner)
withKeyCreate = createdEvents(withKeyTxIds(1)).head withKeyCreate = createdEvents(withKeyTxIds(0)).head
withKeyDisclosedContract = createEventToDisclosedContract(withKeyCreate) withKeyDisclosedContract = createEventToDisclosedContract(withKeyCreate)
exerciseByKeyError <- ledger exerciseByKeyError <- ledger
.submitAndWait( .submitAndWait(
@ -224,8 +225,8 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
for { for {
contractId <- ledger1.create(party1, Dummy(party1)) contractId <- ledger1.create(party1, Dummy(party1))
transactions <- ledger1.flatTransactionsByTemplateId(WithKey.id, party1) transactions <- ledger1.flatTransactionsByTemplateId(Dummy.id, party1)
create = createdEvents(transactions(1)).head create = createdEvents(transactions(0)).head
disclosedContract = createEventToDisclosedContract(create) disclosedContract = createEventToDisclosedContract(create)
// Submit concurrently two consuming exercise choices (with and without disclosed contract) // Submit concurrently two consuming exercise choices (with and without disclosed contract)
@ -266,13 +267,14 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
for { for {
testContext <- initializeTest(ledger, owner, delegate) testContext <- initializeTest(ledger, owner, delegate)
// Exercise a choice using invalid explicit disclosure (bad contract key) // // TODO ED: Enable once the check is implemented in command interpretation
errorBadKey <- testContext // // Exercise a choice using invalid explicit disclosure (bad contract key)
.exerciseFetchDelegated( // errorBadKey <- testContext
testContext.disclosedContract // .exerciseFetchDelegated(
.update(_.metadata.contractKeyHash := ByteString.copyFromUtf8("badKeyMeta")) // testContext.disclosedContract
) // .update(_.metadata.contractKeyHash := ByteString.copyFromUtf8("BadKeyBadKeyBadKeyBadKeyBadKey00"))
.mustFail("using a mismatching contract key hash in metadata") // )
// .mustFail("using a mismatching contract key hash in metadata")
// Exercise a choice using invalid explicit disclosure (bad ledger time) // Exercise a choice using invalid explicit disclosure (bad ledger time)
errorBadLet <- testContext errorBadLet <- testContext
@ -290,12 +292,13 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
) )
.mustFail("using an invalid disclosed contract payload") .mustFail("using an invalid disclosed contract payload")
} yield { } yield {
assertGrpcError( // // TODO ED: Enable once the check is implemented in command interpretation
errorBadKey, // assertGrpcError(
LedgerApiErrors.ConsistencyErrors.DisclosedContractInvalid, // errorBadKey,
None, // LedgerApiErrors.ConsistencyErrors.DisclosedContractInvalid,
checkDefiniteAnswerMetadata = true, // None,
) // checkDefiniteAnswerMetadata = true,
// )
assertGrpcError( assertGrpcError(
errorBadLet, errorBadLet,
LedgerApiErrors.ConsistencyErrors.DisclosedContractInvalid, LedgerApiErrors.ConsistencyErrors.DisclosedContractInvalid,
@ -370,15 +373,15 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
) )
.mustFail("using a disclosed contract with missing createdAt in contract metadata") .mustFail("using a disclosed contract with missing createdAt in contract metadata")
// // TODO ED: Assert missing contract key hash when ledger side metadata validation is implemented // // TODO ED: Assert missing contract key hash when ledger side metadata validation is implemented
// // Exercise a choice using an invalid disclosed contract (missing key hash in contract metadata for a contract that has a contract key associated) // // Exercise a choice using an invalid disclosed contract (missing key hash in contract metadata for a contract that has a contract key associated)
// errorMissingKeyHash <- testContext // errorMissingKeyHash <- testContext
// .exerciseFetchDelegated( // .exerciseFetchDelegated(
// testContext.disclosedContract.update(_.metadata.contractKeyHash.set(ByteString.EMPTY)) // testContext.disclosedContract.update(_.metadata.contractKeyHash.set(ByteString.EMPTY))
// ) // )
// .mustFail( // .mustFail(
// "using a disclosed contract with missing key hash in contract metadata for a contract that has a contract key associated" // "using a disclosed contract with missing key hash in contract metadata for a contract that has a contract key associated"
// ) // )
} yield { } yield {
assertGrpcError( assertGrpcError(
errorMalformedPayload, errorMalformedPayload,
@ -430,25 +433,40 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
for { for {
testContext <- initializeTest(ledger, owner, delegate) testContext <- initializeTest(ledger, owner, delegate)
_ <- ledger.create(owner, Dummy(owner))
dummyTxs <- ledger.flatTransactionsByTemplateId(Dummy.id, owner)
dummyCreate = createdEvents(dummyTxs(0)).head
dummyDisclosedContract = createEventToDisclosedContract(dummyCreate)
// Exercise a choice using invalid explicit disclosure (bad contract key) // Exercise a choice using invalid explicit disclosure (bad contract key)
_ <- testContext _ <- testContext
.exerciseFetchDelegated( .exerciseFetchDelegated(
testContext.disclosedContract testContext.disclosedContract,
.update(_.metadata.contractKeyHash := ByteString.copyFromUtf8("badKeyMeta")) // Provide a superfluous disclosed contract with mismatching key hash
dummyDisclosedContract
.update(
_.metadata.contractKeyHash := ByteString.copyFromUtf8(
"BadKeyBadKeyBadKeyBadKeyBadKey00"
)
),
) )
// Exercise a choice using invalid explicit disclosure (bad ledger time) // Exercise a choice using invalid explicit disclosure (bad ledger time)
_ <- testContext _ <- testContext
.exerciseFetchDelegated( .exerciseFetchDelegated(
testContext.disclosedContract testContext.disclosedContract,
.update(_.metadata.createdAt := com.google.protobuf.timestamp.Timestamp.of(1, 0)) // Provide a superfluous disclosed contract with mismatching createdAt
dummyDisclosedContract
.update(_.metadata.createdAt := com.google.protobuf.timestamp.Timestamp.of(1, 0)),
) )
// Exercise a choice using invalid explicit disclosure (bad payload) // Exercise a choice using invalid explicit disclosure (bad payload)
_ <- testContext _ <- testContext
.exerciseFetchDelegated( .exerciseFetchDelegated(
testContext.disclosedContract testContext.disclosedContract,
.update(_.arguments := Delegated(delegate, testContext.contractKey).arguments) // Provide a superfluous disclosed contract with mismatching contract arguments
dummyDisclosedContract
.update(_.arguments := Dummy(delegate).arguments),
) )
} yield () } yield ()
}) })
@ -554,8 +572,7 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
"EDFeatureDisabled", "EDFeatureDisabled",
"Submission fails when disclosed contracts provided on feature disabled", "Submission fails when disclosed contracts provided on feature disabled",
allocate(Parties(2)), allocate(Parties(2)),
// TODO ED: Toggle after feature flag implementation enabled = feature => !feature.explicitDisclosure,
// enabled = feature => !feature.explicitDisclosure,
)(implicit ec => { case Participants(Participant(ledger, owner, delegate)) => )(implicit ec => { case Participants(Participant(ledger, owner, delegate)) =>
for { for {
testContext <- initializeTest(ledger, owner, delegate) testContext <- initializeTest(ledger, owner, delegate)
@ -593,7 +610,7 @@ final class ExplicitDisclosureIT extends LedgerTestSuite {
verbose = !normalizedDisclosedContract, verbose = !normalizedDisclosedContract,
) )
) )
createdEvent = createdEvents(txs(1)).head createdEvent = createdEvents(txs(0)).head
disclosedContract = createEventToDisclosedContract(createdEvent) disclosedContract = createEventToDisclosedContract(createdEvent)
_ <- ledger.submitAndWait( _ <- ledger.submitAndWait(
@ -749,9 +766,18 @@ object ExplicitDisclosureIT {
Command.Command.ExerciseByKey( Command.Command.ExerciseByKey(
ExerciseByKeyCommand( ExerciseByKeyCommand(
Some(WithKey.id.unwrap), Some(WithKey.id.unwrap),
Option(Value(Value.Sum.Party(owner.unwrap))), Option(Value(Value.Sum.Party(Party.unwrap(owner)))),
"WithKey_NoOp", "WithKey_NoOp",
Option(Value(Value.Sum.Party(party.unwrap))), Option(
Value(
Value.Sum.Record(
Record(
None,
List(RecordField("", Some(Value(Value.Sum.Party(Party.unwrap(party)))))),
)
)
)
),
) )
) )
), ),

View File

@ -43,6 +43,11 @@ class LedgerApiServer(
timeServiceBackendO: Option[TimeServiceBackend], timeServiceBackendO: Option[TimeServiceBackend],
servicesExecutionContext: ExecutionContextExecutorService, servicesExecutionContext: ExecutionContextExecutorService,
metrics: Metrics, metrics: Metrics,
// TODO ED: Remove flag once explicit disclosure is deemed stable and all
// backing ledgers implement proper validation against malicious clients.
// Currently, we provide this flag outside the HOCON configuration objects
// in order to ensure that participants cannot be configured to accept explicitly disclosed contracts.
explicitDisclosureUnsafeEnabled: Boolean = false,
)(implicit actorSystem: ActorSystem, materializer: Materializer) { )(implicit actorSystem: ActorSystem, materializer: Materializer) {
def owner: ResourceOwner[ApiService] = { def owner: ResourceOwner[ApiService] = {
@ -109,6 +114,7 @@ class LedgerApiServer(
ledgerId, ledgerId,
participantConfig.apiServer, participantConfig.apiServer,
participantId, participantId,
explicitDisclosureUnsafeEnabled,
) )
} yield apiService } yield apiService
} }
@ -127,6 +133,7 @@ class LedgerApiServer(
ledgerId: LedgerId, ledgerId: LedgerId,
apiServerConfig: ApiServerConfig, apiServerConfig: ApiServerConfig,
participantId: Ref.ParticipantId, participantId: Ref.ParticipantId,
explicitDisclosureUnsafeEnabled: Boolean,
)(implicit )(implicit
actorSystem: ActorSystem, actorSystem: ActorSystem,
loggingContext: LoggingContext, loggingContext: LoggingContext,
@ -155,6 +162,7 @@ class LedgerApiServer(
participantId = participantId, participantId = participantId,
authService = authService, authService = authService,
jwtTimestampLeeway = participantConfig.jwtTimestampLeeway, jwtTimestampLeeway = participantConfig.jwtTimestampLeeway,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
) )
} }

View File

@ -55,6 +55,7 @@ object ApiServiceOwner {
authService: AuthService, authService: AuthService,
meteringReportKey: MeteringReportKey = CommunityKey, meteringReportKey: MeteringReportKey = CommunityKey,
jwtTimestampLeeway: Option[JwtTimestampLeeway], jwtTimestampLeeway: Option[JwtTimestampLeeway],
explicitDisclosureUnsafeEnabled: Boolean = false,
)(implicit )(implicit
actorSystem: ActorSystem, actorSystem: ActorSystem,
materializer: Materializer, materializer: Materializer,
@ -114,6 +115,7 @@ object ApiServiceOwner {
userManagementConfig = config.userManagement, userManagementConfig = config.userManagement,
apiStreamShutdownTimeout = config.apiStreamShutdownTimeout, apiStreamShutdownTimeout = config.apiStreamShutdownTimeout,
meteringReportKey = meteringReportKey, meteringReportKey = meteringReportKey,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
)(materializer, executionSequencerFactory, loggingContext) )(materializer, executionSequencerFactory, loggingContext)
.map(_.withServices(otherServices)) .map(_.withServices(otherServices))
apiService <- new LedgerApiService( apiService <- new LedgerApiService(

View File

@ -86,6 +86,7 @@ private[daml] object ApiServices {
userManagementConfig: UserManagementConfig, userManagementConfig: UserManagementConfig,
apiStreamShutdownTimeout: scala.concurrent.duration.Duration, apiStreamShutdownTimeout: scala.concurrent.duration.Duration,
meteringReportKey: MeteringReportKey, meteringReportKey: MeteringReportKey,
explicitDisclosureUnsafeEnabled: Boolean,
)(implicit )(implicit
materializer: Materializer, materializer: Materializer,
esf: ExecutionSequencerFactory, esf: ExecutionSequencerFactory,
@ -259,6 +260,7 @@ private[daml] object ApiServices {
commandExecutor, commandExecutor,
checkOverloaded, checkOverloaded,
metrics, metrics,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
) )
// Note: the command service uses the command submission, command completion, and transaction // Note: the command service uses the command submission, command completion, and transaction
@ -282,6 +284,7 @@ private[daml] object ApiServices {
timeProvider = timeProvider, timeProvider = timeProvider,
ledgerConfigurationSubscription = ledgerConfigurationSubscription, ledgerConfigurationSubscription = ledgerConfigurationSubscription,
metrics = metrics, metrics = metrics,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
) )
val apiPartyManagementService = ApiPartyManagementService.createApiService( val apiPartyManagementService = ApiPartyManagementService.createApiService(

View File

@ -8,6 +8,7 @@ import com.daml.ledger.api.v1.experimental_features.{
CommandDeduplicationFeatures, CommandDeduplicationFeatures,
ExperimentalCommitterEventLog, ExperimentalCommitterEventLog,
ExperimentalContractIds, ExperimentalContractIds,
ExperimentalExplicitDisclosure,
} }
case class LedgerFeatures( case class LedgerFeatures(
@ -17,4 +18,6 @@ case class LedgerFeatures(
contractIdFeatures: ExperimentalContractIds = ExperimentalContractIds.defaultInstance, contractIdFeatures: ExperimentalContractIds = ExperimentalContractIds.defaultInstance,
committerEventLog: ExperimentalCommitterEventLog = committerEventLog: ExperimentalCommitterEventLog =
ExperimentalCommitterEventLog.of(eventLogType = CENTRALIZED), ExperimentalCommitterEventLog.of(eventLogType = CENTRALIZED),
explicitDisclosure: ExperimentalExplicitDisclosure =
ExperimentalExplicitDisclosure.of(supported = false),
) )

View File

@ -208,6 +208,7 @@ private[apiserver] object ApiCommandService {
timeProvider: TimeProvider, timeProvider: TimeProvider,
ledgerConfigurationSubscription: LedgerConfigurationSubscription, ledgerConfigurationSubscription: LedgerConfigurationSubscription,
metrics: Metrics, metrics: Metrics,
explicitDisclosureUnsafeEnabled: Boolean,
)(implicit )(implicit
materializer: Materializer, materializer: Materializer,
executionContext: ExecutionContext, executionContext: ExecutionContext,
@ -232,6 +233,7 @@ private[apiserver] object ApiCommandService {
maxDeduplicationDuration = () => maxDeduplicationDuration = () =>
ledgerConfigurationSubscription.latestConfiguration().map(_.maxDeduplicationDuration), ledgerConfigurationSubscription.latestConfiguration().map(_.maxDeduplicationDuration),
generateSubmissionId = SubmissionIdGenerator.Random, generateSubmissionId = SubmissionIdGenerator.Random,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
) )
} }

View File

@ -43,6 +43,7 @@ private[apiserver] object ApiSubmissionService {
commandExecutor: CommandExecutor, commandExecutor: CommandExecutor,
checkOverloaded: TelemetryContext => Option[state.SubmissionResult], checkOverloaded: TelemetryContext => Option[state.SubmissionResult],
metrics: Metrics, metrics: Metrics,
explicitDisclosureUnsafeEnabled: Boolean,
)(implicit )(implicit
executionContext: ExecutionContext, executionContext: ExecutionContext,
loggingContext: LoggingContext, loggingContext: LoggingContext,
@ -65,6 +66,7 @@ private[apiserver] object ApiSubmissionService {
ledgerConfigurationSubscription.latestConfiguration().map(_.maxDeduplicationDuration), ledgerConfigurationSubscription.latestConfiguration().map(_.maxDeduplicationDuration),
submissionIdGenerator = SubmissionIdGenerator.Random, submissionIdGenerator = SubmissionIdGenerator.Random,
metrics = metrics, metrics = metrics,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
) )
} }

View File

@ -74,7 +74,7 @@ private[apiserver] final class ApiVersionService private (
optionalLedgerId = Some(ExperimentalOptionalLedgerId()), optionalLedgerId = Some(ExperimentalOptionalLedgerId()),
contractIds = Some(ledgerFeatures.contractIdFeatures), contractIds = Some(ledgerFeatures.contractIdFeatures),
committerEventLog = Some(ledgerFeatures.committerEventLog), committerEventLog = Some(ledgerFeatures.committerEventLog),
explicitDisclosure = None, // TODO[ED]: Wire-up with participant configuration flag explicitDisclosure = Some(ledgerFeatures.explicitDisclosure),
) )
), ),
) )

View File

@ -575,6 +575,24 @@ server_conformance_test(
], ],
) )
SERVERS_EXPLICIT_DISCLOSURE = {
"h2database": {
"binary": ":app",
"server_args": ["explicit-disclosure-unsafe-enabled run"],
},
"postgresql": {
"binary": ":conformance-test-postgres-bin",
"server_args": ["explicit-disclosure-unsafe-enabled run -c $(rootpath :postgres.conf)"],
"extra_data": [":postgres.conf"],
},
"oracle": {
"binary": ":conformance-test-oracle-bin",
"tags": oracle_tags,
"server_args": ["explicit-disclosure-unsafe-enabled run -c $(rootpath :oracle.conf)"],
"extra_data": [":oracle.conf"],
},
}
server_conformance_test( server_conformance_test(
name = "conformance-test-explicit-disclosure", name = "conformance-test-explicit-disclosure",
hocon = True, hocon = True,
@ -584,7 +602,32 @@ server_conformance_test(
lf_versions = [ lf_versions = [
"1.dev", "1.dev",
], ],
servers = SERVERS, servers = SERVERS_EXPLICIT_DISCLOSURE,
test_tool_args = [
"--verbose",
"--include=ExplicitDisclosureIT",
# TODO ED: Enable the following test once https://github.com/digital-asset/daml/issues/14200 is solved
"--exclude=ExplicitDisclosureIT:EDDuplicates",
# TODO ED: Enable the following tests once https://github.com/digital-asset/daml/issues/14199 is solved
"--exclude=ExplicitDisclosureIT:EDExerciseByKeyDisclosedContract",
"--exclude=ExplicitDisclosureIT:EDLocalKeyVisibility",
"--exclude=ExplicitDisclosureIT:EDNonNormalizedDisclosedContract",
"--exclude=ExplicitDisclosureIT:EDNormalizedDisclosedContract",
],
)
# Suite asserting that tests targeting disabled explicit disclosure are successful
# (i.e. test asserting that submissions using disclosed contracts are rejected)
# TODO ED: Remove once feature deemed stable
conformance_test(
name = "conformance-test-explicit-disclosure-disabled-lf-dev-h2",
hocon = True,
lf_versions = [
"1.dev",
],
ports = [6865],
server = ":app",
server_args = ["run"],
test_tool_args = [ test_tool_args = [
"--verbose", "--verbose",
"--include=ExplicitDisclosureIT", "--include=ExplicitDisclosureIT",

View File

@ -9,6 +9,13 @@ import com.daml.logging.ContextualizedLogger
import com.daml.resources.ProgramResource import com.daml.resources.ProgramResource
object CliSandboxOnXRunner { object CliSandboxOnXRunner {
// TODO ED: Remove flag once explicit disclosure is deemed stable and all
// backing ledgers implement proper validation against malicious clients
//
// NOTE: The flag is explicitly extracted out of the provided program arguments
// and not passed via the HOCON config in order to prevent accidental
// enablement, which could render participants vulnerable to malicious clients.
private val ExplicitDisclosureEnabledArg = "explicit-disclosure-unsafe-enabled"
private val logger = ContextualizedLogger.get(getClass) private val logger = ContextualizedLogger.get(getClass)
val RunnerName = "sandbox-on-x" val RunnerName = "sandbox-on-x"
@ -19,14 +26,23 @@ object CliSandboxOnXRunner {
args: collection.Seq[String], args: collection.Seq[String],
manipulateConfig: CliConfig[BridgeConfig] => CliConfig[BridgeConfig] = identity, manipulateConfig: CliConfig[BridgeConfig] => CliConfig[BridgeConfig] = identity,
): Unit = { ): Unit = {
val explicitDisclosureEnabled = args.contains(ExplicitDisclosureEnabledArg)
val config = CliConfig val config = CliConfig
.parse(RunnerName, BridgeConfig.Parser, BridgeConfig.Default, args) .parse(
RunnerName,
BridgeConfig.Parser,
BridgeConfig.Default,
args.filterNot(_ == ExplicitDisclosureEnabledArg),
)
.map(manipulateConfig) .map(manipulateConfig)
.getOrElse(sys.exit(1)) .getOrElse(sys.exit(1))
runProgram(config) runProgram(config, explicitDisclosureEnabled)
} }
private def runProgram(config: CliConfig[BridgeConfig]): Unit = private def runProgram(
config: CliConfig[BridgeConfig],
explicitDisclosureUnsafeEnabled: Boolean,
): Unit =
config.mode match { config.mode match {
case Mode.Run => case Mode.Run =>
SandboxOnXConfig SandboxOnXConfig
@ -34,7 +50,9 @@ object CliSandboxOnXRunner {
.fold( .fold(
System.err.println, System.err.println,
{ sandboxOnXConfig => { sandboxOnXConfig =>
program(sox(new BridgeConfigAdaptor, sandboxOnXConfig)) program(
sox(new BridgeConfigAdaptor, sandboxOnXConfig, explicitDisclosureUnsafeEnabled)
)
}, },
) )
case Mode.DumpIndexMetadata(jdbcUrls) => case Mode.DumpIndexMetadata(jdbcUrls) =>
@ -46,12 +64,13 @@ object CliSandboxOnXRunner {
case Mode.RunLegacyCliConfig => case Mode.RunLegacyCliConfig =>
val configAdaptor: BridgeConfigAdaptor = new BridgeConfigAdaptor val configAdaptor: BridgeConfigAdaptor = new BridgeConfigAdaptor
val sandboxOnXConfig: SandboxOnXConfig = SandboxOnXConfig.fromLegacy(configAdaptor, config) val sandboxOnXConfig: SandboxOnXConfig = SandboxOnXConfig.fromLegacy(configAdaptor, config)
program(sox(configAdaptor, sandboxOnXConfig)) program(sox(configAdaptor, sandboxOnXConfig, explicitDisclosureUnsafeEnabled))
} }
private def sox( private def sox(
configAdaptor: BridgeConfigAdaptor, configAdaptor: BridgeConfigAdaptor,
sandboxOnXConfig: SandboxOnXConfig, sandboxOnXConfig: SandboxOnXConfig,
explicitDisclosureUnsafeEnabled: Boolean,
): ResourceOwner[Unit] = { ): ResourceOwner[Unit] = {
Banner.show(Console.out) Banner.show(Console.out)
logger.withoutContext.info( logger.withoutContext.info(
@ -62,6 +81,7 @@ object CliSandboxOnXRunner {
configAdaptor, configAdaptor,
sandboxOnXConfig.ledger, sandboxOnXConfig.ledger,
sandboxOnXConfig.bridge, sandboxOnXConfig.bridge,
explicitDisclosureUnsafeEnabled,
) )
.map(_ => ()) .map(_ => ())
} }

View File

@ -21,6 +21,7 @@ import com.daml.ledger.api.v1.experimental_features.{
CommandDeduplicationPeriodSupport, CommandDeduplicationPeriodSupport,
CommandDeduplicationType, CommandDeduplicationType,
ExperimentalContractIds, ExperimentalContractIds,
ExperimentalExplicitDisclosure,
} }
import com.daml.ledger.offset.Offset import com.daml.ledger.offset.Offset
import com.daml.ledger.participant.state.index.v2.IndexService import com.daml.ledger.participant.state.index.v2.IndexService
@ -56,16 +57,20 @@ object SandboxOnXRunner {
configAdaptor: BridgeConfigAdaptor, configAdaptor: BridgeConfigAdaptor,
config: Config, config: Config,
bridgeConfig: BridgeConfig, bridgeConfig: BridgeConfig,
explicitDisclosureUnsafeEnabled: Boolean = false,
): ResourceOwner[Port] = ): ResourceOwner[Port] =
new ResourceOwner[Port] { new ResourceOwner[Port] {
override def acquire()(implicit context: ResourceContext): Resource[Port] = override def acquire()(implicit context: ResourceContext): Resource[Port] =
SandboxOnXRunner.run(bridgeConfig, config, configAdaptor).acquire() SandboxOnXRunner
.run(bridgeConfig, config, configAdaptor, explicitDisclosureUnsafeEnabled)
.acquire()
} }
def run( def run(
bridgeConfig: BridgeConfig, bridgeConfig: BridgeConfig,
config: Config, config: Config,
configAdaptor: BridgeConfigAdaptor, configAdaptor: BridgeConfigAdaptor,
explicitDisclosureUnsafeEnabled: Boolean,
): ResourceOwner[Port] = newLoggingContext { implicit loggingContext => ): ResourceOwner[Port] = newLoggingContext { implicit loggingContext =>
implicit val actorSystem: ActorSystem = ActorSystem(RunnerName) implicit val actorSystem: ActorSystem = ActorSystem(RunnerName)
implicit val materializer: Materializer = Materializer(actorSystem) implicit val materializer: Materializer = Materializer(actorSystem)
@ -111,6 +116,7 @@ object SandboxOnXRunner {
contractIdFeatures = ExperimentalContractIds.of( contractIdFeatures = ExperimentalContractIds.of(
v1 = ExperimentalContractIds.ContractIdV1Support.NON_SUFFIXED v1 = ExperimentalContractIds.ContractIdV1Support.NON_SUFFIXED
), ),
explicitDisclosure = ExperimentalExplicitDisclosure.of(explicitDisclosureUnsafeEnabled),
), ),
authService = configAdaptor.authService(participantConfig), authService = configAdaptor.authService(participantConfig),
buildWriteService = buildWriteServiceLambda, buildWriteService = buildWriteServiceLambda,
@ -127,6 +133,7 @@ object SandboxOnXRunner {
timeServiceBackendO = timeServiceBackendO, timeServiceBackendO = timeServiceBackendO,
servicesExecutionContext = servicesExecutionContext, servicesExecutionContext = servicesExecutionContext,
metrics = metrics, metrics = metrics,
explicitDisclosureUnsafeEnabled = explicitDisclosureUnsafeEnabled,
)(actorSystem, materializer).owner )(actorSystem, materializer).owner
} yield { } yield {
logInitializationHeader( logInitializationHeader(