Test Templates using Daml Script¶
- Allocating parties
- Submitting transactions
- Creating contracts
- Testing for failure
- Archiving contracts
- Viewing ledger and final ledger state
Remember that you can load all the code for this section into a folder called
daml-intro-2 by running
daml new intro2 --template daml-intro-2
Script is like a recipe for a test, where you can script different parties submitting a series of transactions, to check that your templates behave as you’d expect. You can also script some external information like party identities, and ledger time.
Below is a basic script that creates a
Token for a party called “Alice”.
token_test_1 = script do alice <- allocateParty "Alice" submit alice do createCmd Token with owner = alice
You declare a
Script as a top-level variable and introduce it using
do always starts a block, so the rest of the script is indented.
Before you can create any
Token contracts, you need some parties on the test ledger. The above script uses the function
allocateParty to put a party called “Alice” in a variable
alice. There are two things of note there:
The reason for that is
Actionthat can only be performed once the
Scriptis run in the context of a ledger.
<-means “run the action and bind the result”. It can only be run in that context because, depending on the ledger state the script is running on,
allocatePartywill either give you back a party with the name you specified or append a suffix to that name if such a party has already been allocated.
doblocks in Add Constraints to a Contract.
If that doesn’t quite make sense yet, for the time being you can think of this arrow as extracting the right-hand-side value from the ledger and storing it into the variable on the left.
allocatePartydoes not have to be enclosed in brackets. Functions in Daml are called using the syntax
fn arg1 arg2 arg3.
With a variable
alice of type
Party in hand, you can submit your first transaction. Unsurprisingly, you do this using the
submit takes two arguments: the
Party and the
Script is a recipe for a test,
Commands is a recipe for a transaction.
createCmd Token with owner = alice is a
Commands, which translates to a list of commands that will be submitted to the ledger creating a transaction which creates a
Token with owner Alice.
You’ll learn all about the syntax
Token with owner = alice in Data Types.
You could write this as
submit alice (createCmd Token with owner = alice), but just like scripts, you can assemble commands using
do blocks. A
do block always takes the value of the last statement within it so the syntax shown in the commands above gives the same result, whilst being easier to read. Note however, that the commands submitted as part of a transaction are not allowed to depend on each other.
Run the Scripts¶
There are a few ways to run Daml Scripts:
- In Daml Studio against a test ledger, providing visualizations of the resulting ledger.
- Using the command line
daml testalso against a test ledger, useful for continuous integration.
- Against a real ledger, take a look at the documentation for Daml Script for more information.
- Interactively using Daml REPL.
In Daml Studio, you should see the text “Script results” just above the line
token_test_1 = do. Click on it to display the outcome of the script.
This opens the script view in a separate column in VS Code. The default view is a tabular representation of the final state of the ledger:
What this display means:
- The big title reading
Token_Test:Tokenis the identifier of the type of contract that’s listed below.
Token_Testis the module name,
Tokenthe template name.
- The first column shows the ID of the contract. This will be explained later.
- The second column shows the status of the contract, either
- The next section of columns show the contract arguments, with one column per field. As expected, field
'Alice'. The single quotation marks indicate that
Aliceis a party.
- The remaining columns, labelled vertically, show which parties know about which contracts. In this simple script, the sole party “Alice” knows about the contract she created.
To run the same test from the command line, save your module in a file
Token_Test.daml and run
daml damlc -- test --files Token_Test.daml. If your file contains more than one script, all of them will be run.
Test for Failure¶
In Basic Contracts you learned that creating a
Token requires the authority of its owner. In other words, it should not be possible for Alice to create a Token for another party and vice versa. A reasonable attempt to test that would be:
failing_test_1 = do alice <- allocateParty "Alice" bob <- allocateParty "Bob" submit alice do createCmd Token with owner = bob submit bob do createCmd Token with owner = alice
However, if you open the script view for that script, you see the following message:
The script failed, as expected, but scripts abort at the first failure. This means that it only tested that Alice can’t create a token for Bob, and the second
submit statement was never reached.
To test for failing submits and keep the script running thereafter, or fail if the submission succeeds, you can use the
token_test_2 = do alice <- allocateParty "Alice" bob <- allocateParty "Bob" submitMustFail alice do createCmd Token with owner = bob submitMustFail bob do createCmd Token with owner = alice submit alice do createCmd Token with owner = alice submit bob do createCmd Token with owner = bob
submitMustFail never has an impact on the ledger so the resulting tabular script view just shows the two Tokens resulting from the successful
submit statements. Note the new column for Bob as well as the visibilities. Alice and Bob cannot see each others’ Tokens.
Archiving contracts works just like creating them, but using
archiveCmd instead of
createCmd takes an instance of a template,
archiveCmd takes a reference to a contract.
References to contracts have the type
ContractId a, where
a is a type parameter representing the type of contract that the ID refers to. For example, a reference to a
Token would be a
archiveCmd the Token Alice has created, you need to get a handle on its contract ID. In scripts, you do this using
<- notation. That’s because the contract ID needs to be retrieved from the ledger. How this works is discussed in Add Constraints to a Contract.
This script first checks that Bob cannot archive Alice’s Token and then Alice successfully archives it:
token_test_3 = do alice <- allocateParty "Alice" bob <- allocateParty "Bob" alice_token <- submit alice do createCmd Token with owner = alice submitMustFail bob do archiveCmd alice_token submit alice do archiveCmd alice_token
Explore the Ledger¶
The resulting script view is empty, because there are no contracts left on the ledger. However, if you want to see the history of the ledger, e.g. to see how you got to that state, tick the “Show archived” box at the top of the ledger view:
You can see that there was a
Token contract, which is now archived, indicated both by the “archived” value in the
status column as well as by a strikethrough.
Click on the adjacent “Show transaction view” button to see the entire transaction graph:
In the Daml Studio script runner, committed transactions are numbered sequentially. The lines starting with
TX indicate that there are three committed transactions, with ids
#2. These correspond to the three
submitMustFail statements in the script.
#0 has one sub-transaction
#0:0, which the arrow indicates is a
create of a
commit X, sub-transaction Y. All transactions have this format in the script runner. However, this format is a testing feature. In general, you should consider Transaction and Contract IDs to be opaque.
The lines above and below
create Token_Test:Token give additional information:
consumed by: #2:0tells you that the contract is archived in sub-transaction
referenced by #2:0tells you that the contract was used in other transactions, and lists their IDs.
disclosed to (since): 'Alice' (#0)tells you who knows about the contract. The fact that
'Alice'appears in the list is equivalent to an
xin the tabular view. The
(#0)gives you the additional information that
Alicelearned about the contract in commit
- Everything following
withshows the create arguments.
To get a better understanding of script, try the following exercises:
- Write a template for a second type of Token.
- Write a script with two parties and two types of tokens, creating one token of each type for each party and archiving one token for each party, leaving one token of each type in the final ledger view.
- In Archive Contracts you tested that Bob cannot archive Alice’s token. Can you guess why the submit fails? How can you find out why the submit fails?
Remember that in Test for Failure we saw a proper error message for a failing submit.