diff --git a/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/tracker/CommandTracker.scala b/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/tracker/CommandTracker.scala index 4d826d5009..e994a776d3 100644 --- a/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/tracker/CommandTracker.scala +++ b/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/tracker/CommandTracker.scala @@ -236,6 +236,11 @@ private[commands] class CommandTracker[Context]( val submissionId = commands.submissionId val commandId = commands.commandId logger.trace(s"Begin tracking of command $commandId for submission $submissionId.") + if (commands.submissionId.isEmpty) { + throw new IllegalArgumentException( + s"The submission id for the command $commandId is empty. This should not happen." + ) + } if (pendingCommands.contains(TrackedCommandKey(submissionId, commandId))) { // TODO return an error identical to the server side duplicate command error once that's defined. throw new IllegalStateException( diff --git a/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/withoutledgerid/CommandClient.scala b/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/withoutledgerid/CommandClient.scala index d37954f067..415cc2b921 100644 --- a/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/withoutledgerid/CommandClient.scala +++ b/ledger/ledger-api-client/src/main/scala/com/digitalasset/ledger/client/services/commands/withoutledgerid/CommandClient.scala @@ -8,6 +8,7 @@ import akka.stream.Materializer import akka.stream.scaladsl.{Flow, Keep, Sink, Source} import com.codahale.metrics.Counter import com.daml.grpc.adapter.ExecutionSequencerFactory +import com.daml.ledger.api.SubmissionIdGenerator import com.daml.ledger.api.domain.LedgerId import com.daml.ledger.api.v1.command_completion_service.CommandCompletionServiceGrpc.CommandCompletionServiceStub import com.daml.ledger.api.v1.command_completion_service.{ @@ -64,6 +65,8 @@ private[daml] final class CommandClient( ], ] + private val submissionIdGenerator: SubmissionIdGenerator = SubmissionIdGenerator.Random + /** Submit a single command. Successful result does not guarantee that the resulting transaction has been written to * the ledger. In order to get that semantic, use [[trackCommands]] or [[trackCommandsUnbounded]]. */ @@ -198,10 +201,15 @@ private[daml] final class CommandClient( throw new IllegalArgumentException( s"Failing fast on submission request of command ${commands.commandId} with invalid ledger ID ${commands.ledgerId} (client expected $ledgerIdToUse)" ) - else if (commands.applicationId != applicationId) + if (commands.applicationId != applicationId) throw new IllegalArgumentException( s"Failing fast on submission request of command ${commands.commandId} with invalid application ID ${commands.applicationId} (client expected $applicationId)" ) + val nonEmptySubmissionId = if (commands.submissionId.isEmpty) { + submissionIdGenerator.generate() + } else { + commands.submissionId + } val updatedDeduplicationPeriod = commands.deduplicationPeriod match { case DeduplicationPeriod.Empty => DeduplicationPeriod.DeduplicationTime( @@ -213,7 +221,12 @@ private[daml] final class CommandClient( ) case existing => existing } - submission.copy(commands = commands.copy(deduplicationPeriod = updatedDeduplicationPeriod)) + submission.copy(commands = + commands.copy( + submissionId = nonEmptySubmissionId, + deduplicationPeriod = updatedDeduplicationPeriod, + ) + ) }) def submissionFlow[Context]( diff --git a/ledger/ledger-api-client/src/test/suite/scala/com/digitalasset/ledger/client/services/commands/CommandTrackerFlowTest.scala b/ledger/ledger-api-client/src/test/suite/scala/com/digitalasset/ledger/client/services/commands/CommandTrackerFlowTest.scala index 6f4a75aff2..9e9e9ea00a 100644 --- a/ledger/ledger-api-client/src/test/suite/scala/com/digitalasset/ledger/client/services/commands/CommandTrackerFlowTest.scala +++ b/ledger/ledger-api-client/src/test/suite/scala/com/digitalasset/ledger/client/services/commands/CommandTrackerFlowTest.scala @@ -152,6 +152,20 @@ class CommandTrackerFlowTest } } + "a command is submitted without a submission id" should { + + "throw an exception" in { + val Handle(submissions, results, _, _) = + runCommandTrackingFlow(allSubmissionsSuccessful) + + submissions.sendNext(newSubmission("", commandId)) + + val actualException = results.expectError() + actualException shouldBe an[IllegalArgumentException] + actualException.getMessage shouldBe s"The submission id for the command $commandId is empty. This should not happen." + } + } + "the stream fails" should { "expose internal state as materialized value" in { @@ -328,6 +342,7 @@ class CommandTrackerFlowTest CommandSubmission( Commands( commandId = commandId, + submissionId = submissionId, deduplicationPeriod = Commands.DeduplicationPeriod.DeduplicationTime(deduplicationTime), )