Skip to content

Commit

Permalink
Use TO_TEXT_CONTRACT_ID in Show instance of ContractId
Browse files Browse the repository at this point in the history
fixes #7114

This PR changes the Show instance of ContractId and flips the switch
on triggers and DAML Script to run in off-ledger mode.

It also adds a test that for DAML Script we actually get back the
correct contract id.

There is a bit of a design decision here in how we want to print
contract ids, so let me list the options I considered. $cid will stand
for the actual cid and all options are wrapped in markdown inline
code.

1. `"$cid"`. Indistinguishable from string. Suggests that there might
be an IsString instance for ContractId.
2. `<$cid>`. Matches the dummy `<contract-id>` but it’s not a dummy so
I don’t think matching that is benefitial.
3. `$cid`. Easy to spot (contract ids start with # and have no
spaces), clearly not a string but might look slightly weird.

changelog_begin
changelog_end
  • Loading branch information
cocreature committed Aug 17, 2020
1 parent ab8b418 commit 46f41a8
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 10 deletions.
9 changes: 9 additions & 0 deletions compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Version.hs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ featureUnstable = Feature
, featureCppFlag = "DAML_UNSTABLE"
}

featureToTextContractId :: Feature
featureToTextContractId = Feature
{ featureName = "TO_TEXT_CONTRACT_ID primitive"
-- TODO Change as part of #7139
, featureMinVersion = versionDev
, featureCppFlag = "DAML_TO_TEXT_CONTRACT_ID"
}

allFeatures :: [Feature]
allFeatures =
[ featureNumeric
Expand All @@ -130,6 +138,7 @@ allFeatures =
, featureGenMap
, featurePackageMetadata
, featureUnstable
, featureToTextContractId
]

allFeaturesForVersion :: Version -> [Feature]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ convertPrim _ "BETextReplicate" (TInt64 :-> TText :-> TText) = EBuiltin BETextRe
convertPrim _ "BETextSplitOn" (TText :-> TText :-> TList TText) = EBuiltin BETextSplitOn
convertPrim _ "BETextIntercalate" (TText :-> TList TText :-> TText) = EBuiltin BETextIntercalate

-- Conversion from ContractId to Text

convertPrim _ "BEToTextContractId" (TContractId t :-> TOptional TText) =
ETyApp (EBuiltin BEToTextContractId) t


-- Template Desugaring.

convertPrim _ "UCreate" (TCon template :-> TUpdate (TContractId (TCon template')))
Expand Down
9 changes: 8 additions & 1 deletion compiler/damlc/daml-stdlib-src/DA/Internal/LF.daml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,14 @@ data Map a b =
data ContractId a =
ContractId Opaque
instance Eq (ContractId a) where (==) = primitive @"BEEqualContractId"
instance Show (ContractId a) where show _ = "<contract-id>"
instance Show (ContractId a) where
#ifdef DAML_TO_TEXT_CONTRACT_ID
show cid = case primitive @"BEToTextContractId" cid of
None -> "<contract-id>"
Some t -> t
#else
show _ = "<contract-id>"
#endif

-- | HIDE This is currently used in the internals of DAML script and DAML triggers
-- but not really something that we want to expose to users.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,8 @@ class Runner(compiledPackages: CompiledPackages, script: Script.Action, timeMode
esf: ExecutionSequencerFactory,
mat: Materializer): Future[SValue] = {
var clients = initialClients
val machine = Speedy.Machine.fromPureSExpr(extendedCompiledPackages, script.expr)
val machine =
Speedy.Machine.fromPureSExpr(extendedCompiledPackages, script.expr, onLedger = false)

def stepToValue(): Either[RuntimeException, SValue] =
machine.run() match {
Expand Down
62 changes: 56 additions & 6 deletions daml-script/test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,37 @@ EOF
visibility = ["//visibility:public"],
)

# Test DAR in 1.dev to test new features.
genrule(
name = "script-test-1.dev",
srcs =
glob(["**/*.daml"]) + ["//daml-script/daml:daml-script-1.dev.dar"],
outs = ["script-test-1.dev.dar"],
cmd = """
set -eou pipefail
TMP_DIR=$$(mktemp -d)
mkdir -p $$TMP_DIR/daml
cp -L $(location :daml/TestContractId.daml) $$TMP_DIR/daml
cp -L $(location //daml-script/daml:daml-script-1.dev.dar) $$TMP_DIR/
cat << EOF > $$TMP_DIR/daml.yaml
sdk-version: {sdk}
name: script-test-1dev
version: 0.0.1
source: daml
build-options:
- --target=1.dev
dependencies:
- daml-stdlib
- daml-prim
- daml-script-1.dev.dar
EOF
$(location //compiler/damlc) build --project-root=$$TMP_DIR -o $$PWD/$(location script-test-1.dev.dar)
rm -rf $$TMP_DIR
""".format(sdk = sdk_version),
tools = ["//compiler/damlc"],
visibility = ["//visibility:public"],
)

# A variant of script-test that has not been uploaded to the ledger
# to test missing template ids. We only care that this has a different package id.
genrule(
Expand Down Expand Up @@ -93,6 +124,7 @@ da_scala_library(
"//daml-lf/data",
"//daml-lf/interpreter",
"//daml-lf/language",
"//daml-lf/transaction",
"//daml-script/runner:script-runner-lib",
"//language-support/scala/bindings",
"//language-support/scala/bindings-akka",
Expand Down Expand Up @@ -175,28 +207,46 @@ da_scala_test_suite(
client_server_test(
name = "test_static_time",
client = ":test_client_single_participant",
client_files = ["$(rootpath :script-test.dar)"],
data = [":script-test.dar"],
client_files = [
"$(rootpath :script-test.dar)",
"$(rootpath :script-test-1.dev.dar)",
],
data = [
":script-test.dar",
":script-test-1.dev.dar",
],
server = "//ledger/sandbox-classic:sandbox-classic-binary",
server_args = [
"--static-time",
"--port=0",
],
server_files = ["$(rootpath :script-test.dar)"],
server_files = [
"$(rootpath :script-test.dar)",
"$(rootpath :script-test-1.dev.dar)",
],
)

client_server_test(
name = "test_wallclock_time",
client = ":test_client_single_participant",
client_args = ["-w"],
client_files = ["$(rootpath :script-test.dar)"],
data = [":script-test.dar"],
client_files = [
"$(rootpath :script-test.dar)",
"$(rootpath :script-test-1.dev.dar)",
],
data = [
":script-test.dar",
":script-test-1.dev.dar",
],
server = "//ledger/sandbox-classic:sandbox-classic-binary",
server_args = [
"--wall-clock-time",
"--port=0",
],
server_files = ["$(rootpath :script-test.dar)"],
server_files = [
"$(rootpath :script-test.dar)",
"$(rootpath :script-test-1.dev.dar)",
],
)

client_server_test(
Expand Down
18 changes: 18 additions & 0 deletions daml-script/test/daml/TestContractId.daml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

module TestContractId where

import Daml.Script

template T
with
t : Party
where
signatory t

testContractId = do
p <- allocateParty "p"
cid <- submit p (createCmd (T p))
pure (cid, show cid)

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import com.daml.lf.engine.script.{Party => ScriptParty, _}
case class Config(
ledgerPort: Int,
darPath: File,
// 1.dev DAR
devDarPath: File,
wallclockTime: Boolean,
auth: Boolean,
// We use the presence of a root CA as a proxy for whether to enable TLS or not.
Expand Down Expand Up @@ -384,6 +386,28 @@ case class TestAuth(dar: Dar[(PackageId, Package)], runner: TestRunner) {
}
}

case class TestContractId(dar: Dar[(PackageId, Package)], runner: TestRunner) {
val scriptId =
Identifier(dar.main._1, QualifiedName.assertFromString("TestContractId:testContractId"))
def runTests(): Unit = {
runner.genericTest(
"testContractId",
scriptId,
None, {
case SRecord(_, _, vals) if vals.size == 2 => {
(vals.get(0), vals.get(1)) match {
case (SContractId(cid), SText(t)) =>
TestRunner.assertEqual(t, cid.coid, "contract ids")
case (a, b) =>
Left(s"Expected SContractId, SText but got $a, $b")
}
}
case v => Left(s"Expected Tuple2 but got $v")
}
)
}
}

object SingleParticipant {

private val configParser = new scopt.OptionParser[Config]("daml_script_test") {
Expand All @@ -397,6 +421,10 @@ object SingleParticipant {
.required()
.action((d, c) => c.copy(darPath = d))

arg[File]("dev-dar")
.required()
.action((d, c) => c.copy(devDarPath = d))

opt[Unit]('w', "wall-clock-time")
.action { (_, c) =>
c.copy(wallclockTime = true)
Expand Down Expand Up @@ -432,7 +460,7 @@ object SingleParticipant {
}

def main(args: Array[String]): Unit = {
configParser.parse(args, Config(0, null, false, false, None)) match {
configParser.parse(args, Config(0, null, null, false, false, None)) match {
case None =>
sys.exit(1)
case Some(config) =>
Expand All @@ -442,6 +470,13 @@ object SingleParticipant {
case (pkgId, pkgArchive) => Decode.readArchivePayload(pkgId, pkgArchive)
}

println(config.devDarPath)
val encodedDevDar: Dar[(PackageId, DamlLf.ArchivePayload)] =
DarReader().readArchiveFromFile(config.devDarPath).get
val devDar: Dar[(PackageId, Package)] = encodedDevDar.map {
case (pkgId, pkgArchive) => Decode.readArchivePayload(pkgId, pkgArchive)
}

val participantParams = if (config.auth) {
Participants(
None,
Expand Down Expand Up @@ -474,6 +509,8 @@ object SingleParticipant {

val runner =
new TestRunner(participantParams, dar, config.wallclockTime, config.rootCa)
val devRunner =
new TestRunner(participantParams, devDar, config.wallclockTime, config.rootCa)
if (!config.auth) {
TraceOrder(dar, runner).runTests()
Test0(dar, runner).runTests()
Expand All @@ -490,6 +527,7 @@ object SingleParticipant {
TestStack(dar, runner).runTests()
TestMaxInboundMessageSize(dar, runner).runTests()
ScriptExample(dar, runner).runTests()
TestContractId(devDar, devRunner).runTests()
// Keep this at the end since it changes the time and we cannot go backwards.
if (!config.wallclockTime) {
SetTime(dar, runner).runTests()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ class Runner(
getInitialState,
Array(SParty(Party.assertFromString(party)), STimestamp(clientTime), createdValue))
// Prepare a speedy machine for evaluting expressions.
val machine: Speedy.Machine = Speedy.Machine.fromPureSExpr(compiledPackages, initialState)
val machine: Speedy.Machine =
Speedy.Machine.fromPureSExpr(compiledPackages, initialState, onLedger = false)
// Evaluate it.
machine.setExpressionToEvaluate(initialState)
val value = Machine.stepToValue(machine)
Expand Down

0 comments on commit 46f41a8

Please sign in to comment.