DAML Oracles

I recall hearing about smart contract ‘oracles’; external sources of information that play a role in the control flow of the smart contract code execution. Is that supported in DAML in some way? If so, why would that be different to a DB query (which is not advised/possible in DAML)? Question prompted by Party permissions

1 Like

“Oracle” is a pretty broad term for any sort of service that makes external information, like prices, the weather, or time(!) available on a distributed ledger platform. “Making it available” here means storing it on a smart contract so the information can be used by other contracts.

The simplest form of Oracle is just a single (trusted) party making a type of data available on the ledger:

template WeatherReport
  with
    weatherService : Party
    location : Location
    time : Time
    weather : Weather
    subscribers : [Party]
  where
    signatory weatherService
    observer subscribers

A complete Oracle would add a small service to that, which reads weather data from some external source, and periodically creates Weather contracts. To use such Oracle data, you need to decide to trust the issuer. In other words, any contract consuming that reference data needs to specify which oracles to trust.

template RainyDayFund
  with
     amount : Decimal
     owner : Party
     trustedService : Party
     location : Location
  where
    signatory owner

    controller owner can
      Raid : ()
        with
          weatherCid : ContractId WeatherReport
        do
          weather <- fetch weatherCid
          -- check the weather is less than a day old
          assertBefore (addRelTime weather.time (days 1))
          assert (location == weather.location)
         ...

As you can see above, modeling a simple Oracle service in DAML is trivial, which is why there is no special feature for it.

Most Oracle services try to establish trust by having a whole group of parties endorse a piece of information. You could do that by collecting multiple signatures on the same Weather or - my preferred variant - correlating a number of Weather contracts from different services:

template RainyDayFund
  with
    amount : Decimal
    owner : Party
    trustedServices : [Party]
    neededServices : Int
    location : Location
  where
    signatory owner

    controller owner can
      Raid : ()
        with
          weatherCids : [ContractId WeatherReport]
        do
         weathers <-mapA fetch weatherCids
         forA weathers (\weather -> do
           -- check the weathers is less than a day old
           assertBefore (addRelTime weather.time (days 1))
           assert (location == weather.location)
           assert (weather.weatherService `elem` trustedServices))
         -- make sure there are at least neededServices services
         let services = dedup $ map (\w -> w.weatherService) weathers
         assert (length services >= neededServices)

You could make all this a bit fancier still by abstracting the check of the data or using contract key lookups instead of passing in the reference data, but this illustrates what an Oracle service takes on the DAML side.

There is one Oracle that is special though, which DAML treats specially and that we actually used above: Time

Why is time special?

  1. It is fast moving
  2. There isn’t actually any such thing as a single time in a distributed system
  3. Most developers are used to working with system time and are thus accustomed to just having a time to work with

To reconcile these items, DAML ledgers have to establish, as part of consensus, a timestamp for the commit. Commits can’t be guaranteed to be ordered (DAML Ledgers aren’t Blockchains after all), so that timestamp can’t be guaranteed to always move forward. The commit timestamp (called Record Time) is therefore normalized into a reasonably monotonous Ledger Effective TIme. The monotinicity (eg creates happen before exercises) is enforced by the ledger.

In some cases it can still be advantageous to implement you own time Oracle. For example, if you want to have the ability to all mutually agree to turn back time on a contract, you need to take time from a custom time Oracle, rather than the inbuilt one. That can easily be done using the pattern above.

7 Likes

Awesome answer. This should be in the docs :slight_smile:

1 Like