Observers witness consuming exercise consequences

Hi there,
Apologies for the basic question and if this has been asked/explained before.
I still can’t wrap my head around the rationale behind “Observers see consuming choices but not non-consuming ones”. Take the simple example below:

module Basic where
import Daml.Script

template Master
  with
    sig : Party
    obs : Party
    msg : Text
  where
    signatory sig
    observer obs

    choice
      CreateChildConsuming : ContractId Child
      controller sig
      do
        create Child with msg = msg <> " C", ..

    nonconsuming choice
      CreateChildNonconsuming : ContractId Child
      controller sig
      do
        create Child with msg = msg <> " NC", ..

template Child
  with
    sig : Party
    msg : Text
  where
    signatory sig

setup : Script ()
setup = script do
  alice <- allocateParty "Alice"
  bob <- allocateParty "Bob"

  amcid <- submit alice do createCmd Master with sig = alice, obs = bob, msg = "Alice master"

  -- bob learns nothing about the consequences of the exercise
  submit alice do exerciseCmd amcid CreateChildNonconsuming

  -- bob is witness of the consequences of the exercise
  submit alice do exerciseCmd amcid CreateChildConsuming

  return ()

The 2 choices are essentially identical, but with different consuming behavior.

Why do observers need to know (“witness”) the creation of the Child contract when the exercise is consuming but learn nothing when the exercise is non-consuming? To me it would seem logical that observers learns nothing about the created Child contract in either cases as they have nothing to do with the exercise action and the creation of the new contract - and they would learn about the archival of the Master contract when, well, it is archived as a consequence of the consuming “CreateChild” choice.

Many thanks!

Hi @davide_ooz,

The no-qualifier choice behaviour (observers can see consequences) historically precedes the qualified ones (observers don’t see consequences), and changing it at this point would be a massive backwards-compatibility headache.

My recommendation for new code would be to always put a qualifier, unless you explicitly want to opt-in to the observer-observing-consequences behaviour, and in those cases carefully document it.

Note that preconsuming and postconsuming have the same behaviour as nonconsuming when it comes to observing consequences. Also note that you can explicitly call archive within a nonconsuming choice (though from a code readability standpoint I’d strongly recommend using either preconsuming or postconsuming in that case).

Thank you @Gary_Verhaegen, it now makes total sense. It is actually all explained in the choices documentation and in the original blog post about pre-/post- consumability, albeit more as a “side effect” that is easy to overlook. TBH I would emphasize this more in the docs (esp. because of its implications with divulgence) and somehow explicitly recommend using the annotations in new code. I imagine the vast majority of new users would expect/want the “new” behavior, but they would also likely go for the non-annotated choices by default because it seems simpler and there are lots of examples using that semantic. Thank you!

To understand the current behavior it’s useful to look at what the primitives are. There are two main choices here:

  1. Add non-consuming choices and primitive archives. In that case, observers naturally see the archive but not non-consuming choices.
  2. Add non-consuming choices and consuming choices. There is no primitive archive, the only way to archive a contract is via a consuming choice.

Daml currently uses 2. If you call archive you’re not calling some primitive to archive the contract but an autogenerated choice Archive which the compiler adds to every template. Observers have to see the archive which in this setting happens via the consuming exercise. And because privacy is at a subtransaction level, they see the children of that exercise as well.

pre and postconsuming are syntactic sugar for a non-consuming choice which exercises the archive choice at the beginning or end of the choice body.

As Gary mentioned, 1 may have been the better option wtr to chosing the primitives in retrospect.