What are the differences between Observer, Witness and Divulge

G-d willing

I am a bit confused about the differences between the observer, witness, and divulge. What each type can see, and how is it effected when a new contract is created from the original contract they participated?



This is all described in detail in the Privacy section of the Daml documentation.
But let me offer a more concise explanation.
Observers are stakeholders on the contract, who get to see creation and archival of the contract and the exercise of any consuming choice. Observers are able to fetch the contract in Daml templates and to retrieve it via query from the client side.
In certain circumstances a contract may be disclosed to a non-stakeholder. E.g. a controller on a choice gets to see the creation of any contract that happens as a consequence of exercising the choice, even those contracts where the choice controller is not a stakeholder. This disclosure of contracts to non-stakeholders is referred to as “divulgence” and the non-stakeholder party that witnesses the creation of the contract is known as “witness”.
Here’s an example. In the testDivulgence script the Issuer creates an asset with Alice as the owner. Alice then transfers the asset to Bob. Alice is not a stakeholder on the asset owned by Bob. But she gets to see this contract as a witness. If you look at MyAsset contract in the Script Results view in Visual Studio Code and check “Show detailed disclosure” checkbox, you’ll see that the contract is visible to 3 parties: Issuer as a signatory (S), Bob as an observer (O) and Alice as a witness (W).
A witness cannot retrieve the contract from the client side using query functions. But a witness can fetch the contract with the aid of a helper template and the authority of a stakeholder. Note how at the bottom of the script Alice can fetch MyAsset contract owned by Bob, and Charlie cannot.
In practical terms the effect of divulgence is that the action of creating divulged contract is recorded on the participant node that hosts the witness party. Which potentially allows the witness party to read this data by directly accessing the transactions database.

template MyAsset
    issuer : Party
    owner : Party
    amount : Decimal

    signatory issuer
    observer owner

    choice MyAsset_Transfer : ContractId MyAsset
        recipient : Party
      controller owner
        create this with
          owner = recipient

template DivulgenceTestHelper
    issuer : Party
    witness : Party
    signatory issuer
    observer witness

    nonconsuming choice FetchContract : MyAsset
        cid : ContractId MyAsset
      controller witness 
        fetch cid

testDivulgence = script do
  issuer <- allocateParty "Issuer"
  alice <- allocateParty "Alice"
  bob <- allocateParty "Bob"
  charlie <- allocateParty "Charlie"

  aCid <- submit issuer do
    createCmd MyAsset with
      owner = alice
      amount = 5.0

  bCid <- submit alice do
    exerciseCmd aCid MyAsset_Transfer with recipient = bob

  Some bAsset <- queryContractId bob bCid

  testDivulgenceHelperCid <- submit issuer do
    createCmd DivulgenceTestHelper with
      witness = alice
  -- Alice gets to see bCid contract because Alice is a witness
  testDivulgenceAsset <- submit alice do
    exerciseCmd testDivulgenceHelperCid FetchContract with cid = bCid
  testDivulgenceAsset === bAsset

  testDivulgenceHelperCid <- submit issuer do
    createCmd DivulgenceTestHelper with
      witness = charlie
  -- Charlie does not have visibility of bCid contract because Alice is a witness
  testDivulgenceAsset <- submitMustFail charlie do
    exerciseCmd testDivulgenceHelperCid FetchContract with cid = bCid

  return ()

Alex, thanks for a great answer!

In the example, Alice is a witness of MyAsset contract, but not a stakeholder. To fetch the data she needs ContractId of an asset. Is it right, that a single way she can obtain ContractId through a ledger is by a returning value of the Transfer choice? Since she cannot query it…
Or if I set a Java listener on CreatedEven of MyAsset on behalf of Alice she will get a notification after the Transfer action?


G-d willing

Thanks @a_putkov for a great example about the witness and the way it can access the contract.
What about the divulge, how is it created?

You should be able to see the ContractId of MyAsset in the transaction stream. Indeed, if you set a Java listener on behalf of Alice I’d expect it to capture the creation of MyAsset contract as a consequence of the exercise of Transfer choice.

“Divulge” is not a notion that hasn’t been introduced before. “Divulge” is a verb that describes the act of divulgence. In the example I provided MyAsset contract with Bob as owner is divulged to Alice (the witness).

I noticed that there is a warning that this feature is deprecated. Do you know when will it be removed?
And also, what is the plan for Witness when this feature will not be available anymore?
What are the consequences will be for a witness?

I suppose you’re referring to the following warning you can see in the IDE:
“Tried to fetch or exercise <Template> on contract <Contract Id> but none of the reading parties <Reading parties> are contract stakeholders <Contract stakeholder parties>. Use of divulged contracts is deprecated and incompatible with pruning. To remedy, add one of the readers <Reading parties> as an observer to the contract”.
This warning doesn’t say that divulgence is deprecated. What’s deprecated is the deliberate use of divulgence as a disclosure mechanism in Daml applications, because such use of divulgence is incompatible with pruning.
Suppose you have an application that relies on a witness party to be able to fetch a divulged contract (as long as the contract is still active when the witness party tries to fetch it). Suppose this witness party is hosted on its own participant node, where no other parties are hosted. When a contract is divulged to the witness party, the event is logged in the witness party’s participant node. This event is then used by the witness party’s participant node to validate the fetch action, when the witness party submits a command fetching the divulged contract. Preserving the ability of the witness party to fetch the divulged contract requires unbounded storage on the participant node for divulgence events. It’s never safe to prune this event from the witness party’s participant node because this participant node cannot know whether the contract is active or not. The witness party’s participant node is not notified when the contract is archived because the witness party is not privy to the archive action.
The only way to avoid unbounded storage is to include divulgence events in pruning. This, however, results in the behavior when, after pruning the divulgence event, the witness party’s participant node will no longer have any knowledge of the divulged contract and will fail a transaction, where the witness party tries to fetch the contract.
To solve for bounded storage while preserving backwards compatibility for applications that rely on divulgence, pruning of divulgence events was introduced as optional. However, it’s obvious that not pruning divulgence events is not sustainable, hence a deprecation warning was introduced in the IDE when a divulged contract is fetched. It advises you that you cannot rely on fetching the divulged contract by the witness party to check whether the contract is active.

1 Like