Hi @Harsh_Multani,
What this error is trying to tell you (in an admittedly opaque way) is that you are trying to use a function meant for Scripts (query
) in the context of an Update (i.e. from within a template). There is no way (as far as I know) to query for contracts from within a choice; queries are meant to be done by clients before they submit their commands.
Here is a working example using scripts. Note that there is nothing special, as far as Daml is concerned, about upgrades: they are just changing one contract to another. Daml does not know that V2 is a “different version of V1”, as opposed to just any other contract, so for simplicity I have put all the definitions in a single file.
module Main where
import Daml.Script
import DA.Foldable (forA_)
template V1
with
owner: Party
text: Text
where
signatory owner
template V2
with
owner: Party
text: Text
version: Int
where
signatory owner
template UpgradeV1_V2
with
owner: Party
where
signatory owner
nonconsuming choice DoMigrate : ContractId V2
with cid : ContractId V1
controller owner
do
V1{..} <- fetch cid
archive cid
create $ V2 with version = 2, ..
setup : Script ()
setup = do
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
return ()
createContracts : (Text, Text, Int) -> Script ()
createContracts (p, prefix, n) = do
let Some party = partyFromText p
forA_ [1..n] (\i -> do
submit party $ createCmd V1 with owner = party, text = prefix <> show i)
upgradeContracts : Text -> Script ()
upgradeContracts p = do
let Some party = partyFromText p
upgrader <- submit party $ createCmd UpgradeV1_V2 with owner = party
contracts <- query @V1 party
forA_ contracts (\(cid, _) -> do
submit party $ exerciseCmd upgrader DoMigrate with cid)
submit party $ archiveCmd upgrader
If you create a new project with daml new t
and then replace the contents of t/daml/Main.daml
with the above code, you should be able to run through the following sequence.
First, in a separate terminal, run daml start
. This will compile the code, start a sandbox, deploy the code to the sandbox, start navigator, and open up navigator in your default browser. At this point you should have two parties, Bob and Alice, as well as three templates, but no contract regardless of who you log in as.
Now we can run our scripts. Because the command is a bit long, I like making up an abbreviation for it, so I would start with:
run=(daml script --dar .daml/dist/t-0.0.1.dar --ledger-host localhost --ledger-port 6865 --wall-clock-time --script-name)
Afterwards, we can run the setup
script with:
${run[@]} Main:setup
This should run to completion if you’re running it for the first time; subsequent runs will fail saying the parties already exist.
Once the parties exist, we can use the other two scripts. For example, after running:
${run[@]} Main:createContracts --input-file <(echo '["Alice", "prefix", 5]')
you should see 5 Main:V1
contracts in the navigator. If you run:
${run[@]} Main:createContracts --input-file <(echo '["Bob", "bob", 2]')
you should also see 2 Main:V1
contracts if you log out and log back in as Bob.
Finally, if you run:
${run[@]} Main:upgradeContracts --input-file <(echo '"Alice"')
you should see in navigator that all of Alice’s contracts are now at Main:V2
, while Bob’s contracts are unaffected.
If for some reason you do not have access to the gRPC API, that’s not an issue: scripts can run just as well against the JSON API. There is a little bit of setup needed, though. Continuing on the same example, the first step is obviously to start the JSON API. Open up a third terminal to run:
daml json-api --http-port 3000 --ledger-host localhost --ledger-port 6865
Next, you need to create a token for your party. Let’s say we want to run commands as Bob. In order to create a valid token, we need to know the ledger ID, which in this case has been generated by the sandbox at startup. If you scroll up a bit in the sandbox terminal, you should see a line that looks like this:
INFO: Initialized sandbox version 1.11.0-snapshot.20210304.6422.0.d3d5042a with ledger-id = 5a9ac3c8-82e2-490f-bb21-656f0ed4cea0, port = 6865, dar file = List(.daml/dist/t-0.0.1.dar), time mode = wall-clock time, ledger = in-memory, auth-service = AuthServiceWildcard$, contract ids seeding = strong
This tells us that, in m case, the ledger ID is 5a9ac3c8-82e2-490f-bb21-656f0ed4cea0
; it will be different for you. Now that we have the ledger ID, we can create a token by visiting jwt.io and pasting this in the second box on the right (this process is described in more details in the JSON API documentation):
{
"https://daml.com/ledger-api": {
"ledgerId": "5a9ac3c8-82e2-490f-bb21-656f0ed4cea0",
"applicationId": "foobar",
"actAs": [
"Bob"
]
}
}
In my case this gives the following value:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiI1YTlhYzNjOC04MmUyLTQ5MGYtYmIyMS02NTZmMGVkNGNlYTAiLCJhcHBsaWNhdGlvbklkIjoiZm9vYmFyIiwiYWN0QXMiOlsiQm9iIl19fQ.Uo0Y90ngUQAjsDiy8g9ufu2XmbH1chtSY5MC8_gn2OM
which I saved as the bob_token
file:
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiI1YTlhYzNjOC04MmUyLTQ5MGYtYmIyMS02NTZmMGVkNGNlYTAiLCJhcHBsaWNhdGlvbklkIjoiZm9vYmFyIiwiYWN0QXMiOlsiQm9iIl19fQ.Uo0Y90ngUQAjsDiy8g9ufu2XmbH1chtSY5MC8_gn2OM" > bob_token
With the setup out of the way, we can go back to the active terminal (where we ran the daml script
commands via ${run[@]}
) and type, for example;
daml script --ledger-host http://localhost \
--ledger-port 3000 \
--json-api \
--dar .daml/dist/t-0.0.1.dar \
--wall-clock-time \
--script-name Main:upgradeContracts \
--input-file <(echo '"Bob"') \
--access-token-file bob_token
Hope that helped.