Support wallclock time in DAML script (#3472)

fixes #3469
This commit is contained in:
Moritz Kiefer 2019-11-14 17:25:15 +01:00 committed by mergify[bot]
parent b7e2c17863
commit f746b9f6c0
5 changed files with 78 additions and 11 deletions

View File

@ -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
@ -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)
@ -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)(

View File

@ -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 {
@ -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(
@ -44,6 +61,8 @@ object RunnerConfig {
scriptIdentifier = null,
ledgerHost = "",
ledgerPort = 0,
timeProviderType = TimeProviderType.Static,
commandTtl = Duration.ofSeconds(30L),
)
)
}

View File

@ -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}
@ -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 {
@ -45,6 +49,17 @@ 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 =
@ -52,7 +67,7 @@ object RunnerMain {
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)

View File

@ -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)"],
)

View File

@ -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
@ -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 doesnt work nicely with
// the client_server_test macro.
@ -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
@ -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
@ -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) =>
@ -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()