Will there be naming conflicts if I upgrade my contracts on Daml on Fabric (or any ledger)?

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.

1 Like