1

Is it possible to define a template Daily, which can only be created once per day in the sense that if Alice creates one, Bob no longer can, and if Bob creates one, Alice no longer can?

bame
  • 960
  • 5
  • 7

2 Answers2

1

When asking about constraints like "one per day" in DAML, one has to think about the scope of that constraint and who guarantees it.

The simplest possible template in DAML is

template Daily
  with
    holder : Party
  where
    signatory holder

An instance of this template is only known to holder. There is no party, or set of parties that could ensure that there is only one such contract instance between Alice and Bob. In certain ledger topologies, Alice and Bob may not even know about each other, nor is there any party that knows about both.

A set of parties that guarantees the uniqueness is needed:

template Daily
  with
    holder : Party
    uniquenessGuarantors : [Party]
...

The uniqueness guarantors need to be able to enable or block the creation of a Daily. In other words, they need to be signatories.

template Daily
  with
    holder : Party
    uniquenessGuarantors : [Party]
  where
    signatory holder, uniquenessGuarantors

Now the easiest way to guarantee any sort of uniqueness in DAML is by using contract keys. Since we want one per day, we need a Date field.

template Daily
  with
    holder : Party
    uniquenessGuarantors : [Party]
    date : Date
  where
    signatory holder, uniquenessGuarantors
    key (uniquenessGuarantors, date) : ([Party], Date)
    maintainer key._1

What this says is that there is a unique copy of Daily for each key, and the guarantors in key._1 are responsible for making it so.

Finally you need a mechanism for actually creating these things, a sort of DailyFactory provided by the guarantors. That factory can also take care of making sure that date is always set to the current date on the ledger.

template DailyFactory
  with
    uniquenessGuarantors : [Party]
    holder : Party
  where
    signatory uniquenessGuarantors

    controller holder can
      nonconsuming FabricateDaily
        : ContractId Daily
        do
          now <- getTime
          let date = toDateUTC now
          create Daily with ..

A simple test shows how it works, with uniqueness being guaranteed by a single party Charlie:

test_daily = scenario do
  [alice, bob, charlie] <- mapA getParty ["Alice", "Bob", "Charlie"]
  fAlice <- submit charlie do
    create DailyFactory with
      holder = alice
      uniquenessGuarantors = [charlie]
  fBob <- submit charlie do
    create DailyFactory with
      holder = bob
      uniquenessGuarantors = [charlie]

  -- Alice can get hold of a `Daily`
  submit alice do
    exercise fAlice FabricateDaily

  -- Neither can create a second
  submitMustFail alice do
    exercise fAlice FabricateDaily
  submitMustFail bob do
    exercise fBob FabricateDaily

  -- The next day bob can create one
  pass (days 1)

  submit bob do
    exercise fBob FabricateDaily

  -- But neither can create a second
  submitMustFail alice do
    exercise fAlice FabricateDaily
  submitMustFail bob do
    exercise fBob FabricateDaily

Note that in terms of privacy, Alice and Bob don't know about each other or the other's Daily or DailyFactory, but the uniquenessGuarantors know all parties for which uniqueness is maintained, and know of all Daily instances for which they guarantee uniqueness. They have to!

To run the above snippets, you need to import DA.Time and DA.Date.

bame
  • 960
  • 5
  • 7
0

Beware that getTime returns UTC - and consequently the code would guarantee uniqueness, one per day, according to UTC, but not for example according to local calendar (which could be, say, Auckland NZ).