daml icon indicating copy to clipboard operation
daml copied to clipboard

`createCmd <interface value>` crashes at runtime

Open akrmn opened this issue 3 years ago • 3 comments

In SDK 2.4.2, createCmd : Template t => t -> Commands (ContractId t) only accepts templates because the definition of class Template before #15347 required HasAgreement, which interfaces do not have. After #15347, the definition of Template has been relaxed, so now Template x also holds for any interface x, so now the type checker allows using createCmd with an interface value as argument. However, this now crashes the scenario service with "SValue.toValue: unexpected SAny"

To reproduce

  1. Install daml from head with daml-sdk-head
  2. Place the files in a new directory
  3. Run daml test from that directory

Expectation

  1. test passes

Reality

  1. test crashes with
2022-12-08 17:33:57.14 [ERROR]  [test ScenarioService] 
SCENARIO SERVICE STDERR: com.daml.lf.speedy.SError$SErrorCrash: CRASH (com.daml.lf.speedy.SValue.go): SValue.toValue: unexpected SAny
File:     Main.daml
Hidden:   no
Range:    18:1-18:5
Source:   Script
Severity: DsError
Message: 
  "Scenario service backend error: BErrorClient (ClientIOError (GRPCIOBadStatusCode StatusUnknown
  (StatusDetails {unStatusDetails = \"\"})))"

Files

-- Main.daml
module Main where

import Daml.Script

interface I
  where
    viewtype T

template T
  with
    p : Party
  where
    signatory p
    interface instance I for T where
      view = this

test : Script ()
test = do
  alice <- allocateParty "alice"
  alice `submit` do
    createCmd $
      toInterface @I T with
        p = alice
  pure ()
# daml.yaml
sdk-version: 0.0.0
build-options: [--target=1.15]
name: create-cmd-interface
source: Main.daml
version: 0.0.1
dependencies:
  - daml-prim
  - daml-stdlib
  - daml-script

akrmn avatar Dec 08 '22 17:12 akrmn

Similarly,

  1. queryContractId : (Template t, IsParties p, HasCallStack) => p -> ContractId t -> Script (Optional t) can be used with t ~ <some interface>, crashing at runtime; and
  2. query : (Template t, IsParties p) => p -> Script [(ContractId t, t)] can be used with t ~ <some interface>, returning the empty list (regardless of the existence of contracts with the requested interface on the ledger)

akrmn avatar Jan 09 '23 15:01 akrmn

We should constrain the type to be templates only, possibly by restricting it to HasAgreement as a short term solution. Long term (or short term if feasible), we want a nicer solution to restrict it to templates in a way that's not dependent on HasAgreement.

basvangijzel-DA avatar Jan 10 '23 15:01 basvangijzel-DA

the difficulty here is in naming; we could well add a compiler typeclass e.g. IsTemplate t but we already have a type class synonym Template t•, and it would be hard to explain to users when to use one or the other.

  • https://github.com/digital-asset/daml/blob/b3f37dee04de3cbd13b6a92892c9e1f7c1c91691/sdk/compiler/damlc/daml-stdlib-src/DA/Internal/Template/Functions.daml#L23-L27

If we had another term for the existing Template t type class synonym, e.g. LedgerType t, we could then define Template t (and Interface t while we're at it) as an empty class with LedgerType t as superclass, with the compiler generating the appropriate instances while forbidding manually-written ones. Functions that only work with templates (resp. interfaces) would have the constraint Template t => (resp. Interface t =>), while functions that work with either would have LedgerType t =>.

type LedgerType t =
  ( HasTemplateTypeRep t
  , HasToAnyTemplate t
  , HasFromAnyTemplate t
  )

class LedgerType t => Template t
class LedgerType t => Interface t

However, I'm not satisfied with the name LedgerType and, with backwards compatibility in mind, this would have to be a multi-step process to first deprecate the existing Template type class synonym and later introduce the new definitions.

moisesackerman-da avatar Apr 25 '24 15:04 moisesackerman-da