Skip to content

Commit

Permalink
Support wallclock time in DAML script (digital-asset#3472)
Browse files Browse the repository at this point in the history
  • Loading branch information
cocreature authored and mergify[bot] committed Nov 14, 2019
1 parent b7e2c17 commit f746b9f
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ package com.digitalasset.daml.lf.engine.script
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import com.typesafe.scalalogging.StrictLogging
import java.time.Instant
import java.util.UUID
import scala.concurrent.{ExecutionContext, Future}
import scalaz.std.either._
import scalaz.syntax.tag._
import scalaz.syntax.traverse._

import com.digitalasset.api.util.TimestampConversion.fromInstant
import com.digitalasset.daml.lf.PureCompiledPackages
import com.digitalasset.daml.lf.archive.Dar
import com.digitalasset.daml.lf.data.FrontStack
Expand All @@ -34,8 +32,13 @@ import com.digitalasset.ledger.api.v1.transaction_filter.{
InclusiveFilters
}
import com.digitalasset.ledger.client.LedgerClient
import com.digitalasset.ledger.client.services.commands.CommandUpdater

class Runner(dar: Dar[(PackageId, Package)], applicationId: ApplicationId) extends StrictLogging {
class Runner(
dar: Dar[(PackageId, Package)],
applicationId: ApplicationId,
commandUpdater: CommandUpdater)
extends StrictLogging {

val darMap: Map[PackageId, Package] = dar.all.toMap
val compiler = Compiler(darMap)
Expand Down Expand Up @@ -97,10 +100,10 @@ class Runner(dar: Dar[(PackageId, Package)], applicationId: ApplicationId) exten
ledgerId = ledgerId.unwrap,
applicationId = applicationId.unwrap,
commandId = UUID.randomUUID.toString,
ledgerEffectiveTime = Some(fromInstant(Instant.EPOCH)),
maximumRecordTime = Some(fromInstant(Instant.EPOCH.plusSeconds(5)))
ledgerEffectiveTime = None,
maximumRecordTime = None,
)
SubmitAndWaitRequest(Some(commands))
SubmitAndWaitRequest(Some(commandUpdater.applyOverrides(commands)))
}

def run(client: LedgerClient, scriptId: Identifier)(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
package com.digitalasset.daml.lf.engine.script

import java.io.File
import java.time.Duration

import com.digitalasset.platform.services.time.TimeProviderType

case class RunnerConfig(
darPath: File,
scriptIdentifier: String,
ledgerHost: String,
ledgerPort: Int,
timeProviderType: TimeProviderType,
commandTtl: Duration,
)

object RunnerConfig {
Expand All @@ -35,6 +40,18 @@ object RunnerConfig {
.required()
.action((t, c) => c.copy(ledgerPort = t))
.text("Ledger port")

opt[Unit]('w', "wall-clock-time")
.action { (t, c) =>
c.copy(timeProviderType = TimeProviderType.WallClock)
}
.text("Use wall clock time (UTC). When not provided, static time is used.")

opt[Long]("ttl")
.action { (t, c) =>
c.copy(commandTtl = Duration.ofSeconds(t))
}
.text("TTL in seconds used for commands emitted by the trigger. Defaults to 30s.")
}
def parse(args: Array[String]): Option[RunnerConfig] =
parser.parse(
Expand All @@ -44,6 +61,8 @@ object RunnerConfig {
scriptIdentifier = null,
ledgerHost = "",
ledgerPort = 0,
timeProviderType = TimeProviderType.Static,
commandTtl = Duration.ofSeconds(30L),
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ package com.digitalasset.daml.lf.engine.script

import akka.actor.ActorSystem
import akka.stream._
import java.time.Instant
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration
import scalaz.syntax.traverse._

import com.digitalasset.api.util.TimeProvider
import com.digitalasset.daml.lf.archive.{Dar, DarReader}
import com.digitalasset.daml.lf.archive.Decode
import com.digitalasset.daml.lf.data.Ref.{Identifier, PackageId, QualifiedName}
Expand All @@ -22,6 +24,8 @@ import com.digitalasset.ledger.client.configuration.{
LedgerClientConfiguration,
LedgerIdRequirement
}
import com.digitalasset.ledger.client.services.commands.CommandUpdater
import com.digitalasset.platform.services.time.TimeProviderType

object RunnerMain {

Expand All @@ -45,14 +49,25 @@ object RunnerMain {
commandClient = CommandClientConfiguration.default,
sslContext = None
)
val timeProvider: TimeProvider =
config.timeProviderType match {
case TimeProviderType.Static => TimeProvider.Constant(Instant.EPOCH)
case TimeProviderType.WallClock => TimeProvider.UTC
case _ =>
throw new RuntimeException(s"Unexpected TimeProviderType: $config.timeProviderType")
}
val commandUpdater = new CommandUpdater(
timeProviderO = Some(timeProvider),
ttl = config.commandTtl,
overrideTtl = true)

val system: ActorSystem = ActorSystem("ScriptRunner")
implicit val sequencer: ExecutionSequencerFactory =
new AkkaExecutionSequencerPool("ScriptRunnerPool")(system)
implicit val ec: ExecutionContext = system.dispatcher
implicit val materializer: ActorMaterializer = ActorMaterializer()(system)

val runner = new Runner(dar, applicationId)
val runner = new Runner(dar, applicationId, commandUpdater)
val flow: Future[Unit] = for {
client <- LedgerClient.singleHost(config.ledgerHost, config.ledgerPort, clientConfig)
_ <- runner.run(client, scriptId)
Expand Down
14 changes: 13 additions & 1 deletion daml-script/test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,16 @@ client_server_test(
server_files = ["$(rootpath :script-test.dar)"],
)

# TODO Add wallclock time
client_server_test(
name = "test_wallclock_time",
client = ":test_client",
client_args = ["-w"],
client_files = ["$(rootpath :script-test.dar)"],
data = [":script-test.dar"],
server = "//ledger/sandbox:sandbox-binary",
server_args = [
"-w",
"--port=0",
],
server_files = ["$(rootpath :script-test.dar)"],
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import akka.actor.ActorSystem
import akka.stream._
import com.typesafe.scalalogging.StrictLogging
import java.io.File
import java.time.Instant
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration
import scala.util.{Success, Failure}
import scalaz.syntax.tag._
import scalaz.syntax.traverse._

import com.digitalasset.api.util.TimeProvider
import com.digitalasset.daml.lf.archive.Dar
import com.digitalasset.daml.lf.archive.DarReader
import com.digitalasset.daml.lf.archive.Decode
Expand All @@ -30,10 +32,11 @@ import com.digitalasset.ledger.client.configuration.{
LedgerIdRequirement
}
import com.digitalasset.ledger.client.LedgerClient
import com.digitalasset.ledger.client.services.commands.CommandUpdater

import com.digitalasset.daml.lf.engine.script.Runner

case class Config(ledgerPort: Int, darPath: File)
case class Config(ledgerPort: Int, darPath: File, wallclockTime: Boolean)

// We do not use scalatest here since that doesn’t work nicely with
// the client_server_test macro.
Expand Down Expand Up @@ -64,6 +67,15 @@ class TestRunner(val config: Config) extends StrictLogging {
commandClient = CommandClientConfiguration.default,
sslContext = None
)
val ttl = java.time.Duration.ofSeconds(30)
val commandUpdater = if (config.wallclockTime) {
new CommandUpdater(timeProviderO = Some(TimeProvider.UTC), ttl = ttl, overrideTtl = true)
} else {
new CommandUpdater(
timeProviderO = Some(TimeProvider.Constant(Instant.EPOCH)),
ttl = ttl,
overrideTtl = true)
}

def genericTest[A](
// test name
Expand All @@ -84,7 +96,7 @@ class TestRunner(val config: Config) extends StrictLogging {

val clientF = LedgerClient.singleHost("localhost", config.ledgerPort, clientConfig)

val runner = new Runner(dar, applicationId)
val runner = new Runner(dar, applicationId, commandUpdater)

val testFlow: Future[Unit] = for {
client <- clientF
Expand Down Expand Up @@ -205,12 +217,17 @@ object TestMain {
.required()
.action((d, c) => c.copy(darPath = d))

opt[Unit]('w', "wall-clock-time")
.action { (t, c) =>
c.copy(wallclockTime = true)
}
.text("Use wall clock time (UTC). When not provided, static time is used.")
}

private val applicationId = ApplicationId("DAML Script Tests")

def main(args: Array[String]): Unit = {
configParser.parse(args, Config(0, null)) match {
configParser.parse(args, Config(0, null, false)) match {
case None =>
sys.exit(1)
case Some(config) =>
Expand All @@ -219,6 +236,7 @@ object TestMain {
val dar: Dar[(PackageId, Package)] = encodedDar.map {
case (pkgId, pkgArchive) => Decode.readArchivePayload(pkgId, pkgArchive)
}

val runner = new TestRunner(config)
Test0(dar, runner).runTests()
Test1(dar, runner).runTests()
Expand Down

0 comments on commit f746b9f

Please sign in to comment.