Programing for upgradeability and an upgrade proposal

I was thinking about the upgrade scenario of a multi signatory contract where one party is the provider of a service. Imagine that a service provider creates an agreement with other users along the lines of

template UserRole
  with
    provider : Party
    user : Party
  where
    signatory user, provider

    controller user can
      RequestToDoStuff : (ContractId RequestForDoneStuff, ContractId WorkProcessOrder)
        with
          input : Text
          workId : Int
        do
          w <- create WorkProcessOrder with ..
          r <- create RequestForDoneStuff with ..
          return (w,r)

where WorkProcessOrder is a contract signed just by the provider to track the work order that user witnesses (“don’t worry, I’ll be busy doing this thing”). The provider might create this contract to track the evolution of the work that user requests, but to hide the process from them (ex. the process is proprietary, or maybe just private to provider). The intent being that once they’re finished, they can exercise a choice on RequestForDoneStuff.

Now the provider is innovating and has come up with a new process and wants to update this contract. The standard upgrade story is ok, but it can be onerous for the provider as they have many users and many orders. Supporting two processes, let alone any not upgraded contracts and previous processes, will stifle the innovation that provider provides. So what does provider do?

Of course they berate their Daml developers (DA CX engineers, obviously :smiley: ) who originally wrote RequestToDoStuff for including the creation of WorkProcessOrder in the first place. Question, wouldn’t it have been better to leave that create off in the first place and replace it with a trigger? To which that developer mumbles back that Daml’s composable, atomic transactions, less code to maintain, boo triggers…etc. We should obviously think a little bit about the future evolution of our code, but it seems particularly relevant to think about what templates might be upgrade and the ease of doing that.

When I consider this scenario, I think that there’s one weak rule of upgrades that single parties should be allowed to impose on other parties within a multi-signatory setting: A party P should be able to upgrade a contract from template T1 to T2 if there’s a 1-1 mapping between the choices in T1 to T2, and for a choice C, P can change what contracts CP witnesses within C (and nothing else). If CP witnesses WorkProcessOrder they still just witness it; they cannot be forced to be an observer or signatory. I call this the weak version as I am not convinced of any of the stronger versions, so let’s start here. Obviously, this is just a proposal, what do you think?

1 Like

Imagine we had this:

template Iou
  with
    leonid : Party
    bernhard : Party
  where
    signatory leonid, bernhard
  
    controller leonid can
      Redeem : (ContractId Cash, ContractId RedemptionNotice)
        with
          cash : ContractId Cash
        do
            c <- exercise cash Transfer with newOwner = leonid
            n <- create RedemptionNotice with ..

You have a right to exercise this choice, and having this choice has obvious value. You get cash. Now do you really want to allow me to unilaterally switch our RedemptionNotice with

template RedemptionNotice
  with
     bernhard : Party
  where
    signatory bernhard
    ensure False

The right way to do this (until we have something like interfaces) is to separate the joint part of the user role and the party-specific. In this case, that probably boils down to not needing the user to sign the UserRole. Since they are the ones calling all their choices, you get their authority through them being the controller. Once you do that, the provider can just switch out the user role at which point the user should do their due diligence on the changed contract.

2 Likes

This is a good example of the subtlety of the problem, thank you.

Is this situation possible without an upgrade? No, because we rely on the user/leonid being able to audit all of the code for the transactions within WorkProcessOrder/ Redeem?

1 Like

That’s right. Right now Daml gives the guarantee that at the point you sign anything, you can predict all possible consequences.

This feature makes Daml very safe, but also a bit rigid. Getting the balance between safety and rigidity right is the really tricky part of making Daml models more dynamic - eg allowing generic libraries or easier upgrading.

It’s something we will definitely address at some point, but it’s too early to talk about the exact mechanism. The way I think about it in the abstract is “authorized interfaces”. Ie you want to abstract over the create RedemptionNotice in such a way that that part becomes easy to switch out - ie call into some IRedemptionNoticeFactory type thing ;). But you don’t quite want me to be able to switch that out unilaterally. Rather, we want to have a way to bilaterally agree that RedemptionNoticeFactoryMkII is an agreed instance of IRedemptionNoticeFactory. Ie I need to pick up one signature from you to switch out/extend the functionality globally.

1 Like

One follow up thought that I had about the UserRole template, is that the provider of the service can always add (or require) an explicit single party Archive-like choice to the contract to deal with users avoid upgrades. That choice changes the semantics of the whole template. It isn’t something one would have on an Iou but a reasonable way to grant more power to one of the signatories.