Is there a way to test witnessing in a DAML Scenario?

Mod note: As of SDK 1.5.0 Scenarios have been superseded by the more powerful Daml Script. We now recommend using that for all purposes. For more information, and to learn how to use Script please check out @Andreas’ post on our blog.

I would like to test if a party is really unable to “see” a specific contract e.g. the party cannot fetch it and is neither a witness (in table view, it should have -). My understanding is that fetch is not enough for this task. I can manually check visibility in the “Table View” but would like to do it programmatically.

How can I do this preferably from a DAML Scenario?

2 Likes

I’m not aware of how you can do it using a scenario. You can be sure about this reading transaction trees on behalf of the user from the Ledger API and verifying you don’t see any event about the contract. This would mean you would have an integration test for it rather than a unit test.

You can do this using fetch. Let’s say you have

template Secret
  with
    p : Party
  where
    signatory p

and want to test whether Bob can see a contract signed by Alice. You can do so by just doing a fetch in a submit block:

d = scenario do
  [alice, bob] <- mapA getParty ["Alice", "Bob"]

  secret <- submit alice do
    create Secret with p = alice

  submit bob do
    fetch secret

This will error with the expected message:

  Attempt to fetch or exercise a contract not visible to the committer.
  Contract:  #0:0 (Main:Secret)
  Committer: 'Bob'
  Disclosed to: 'Alice'

You may now think that you can just change submit to submitMustFail and be done with it, but that’s not the case. Let’s divulge the contract to Bob to see what happens:

template SecretDivulger
  with
    bob : Party
    alice : Party
  where
    signatory bob

    controller alice can
      Divulge : Secret
        with
          secret : ContractId Secret
        do
          fetch secret

d2 = scenario do
  [alice, bob] <- mapA getParty ["Alice", "Bob"]

  secret <- submit alice do
    create Secret with ..

  divulger <- submit bob do
    create SecretDivulger with ..

  submit alice do
    exercise divulger Divulge with ..

  submit bob do
    fetch secret

The scenario still fails, but this time with a different error message:

  0: fetch of Main:Secret at DA.Internal.Prelude:381:26
     failed since none of the stakeholders 'Alice'
     is in the authorizing set 'Bob'

The authorisation check didn’t pass. In other words, our original test would never succeed so changing the submit to submitMustFail doesn’t help at all.

But we can fix the authorization check by delegating the fetch:

template FetchDelegator
  with
    bob : Party
    alice : Party
  where
    signatory alice

    controller bob can
      Fetch : Secret
        with
          secret : ContractId Secret
        do
          fetch secret


d3 = scenario do
  [alice, bob] <- mapA getParty ["Alice", "Bob"]

  secret <- submit alice do
    create Secret with ..

  divulger <- submit bob do
    create SecretDivulger with ..

  submit alice do
    exercise divulger Divulge with ..

  delegator <- submit alice do
    create FetchDelegator with ..

  submit bob do
    exercise delegator Fetch with ..

You can now test that the final submit fails or succeeds exactly in line with whether Bob knows the contract or not.

This is rather a difficult way to test this, which is one of the many use-cases for returning error details on submitMustFail. @matt’s working on that.

3 Likes