Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Add AsData x type constructor and functions to work with it #185

Open
blamario opened this issue Feb 9, 2022 · 14 comments · May be fixed by #191
Open

Add AsData x type constructor and functions to work with it #185

blamario opened this issue Feb 9, 2022 · 14 comments · May be fixed by #191
Assignees

Comments

@blamario
Copy link

blamario commented Feb 9, 2022

The secret of Plutarch's efficiency comes down to two simple rules:

  • don't generate ridiculous code, and
  • postpone Data decoding as long as possible.

There's not much PlutusTx users can do about the former, though Shrinker can help. The second technique, on the other hand, can be translated to PlutusTx thus:

  1. Introduce AsData type constructor to correspond with Plutarch's PAsData, along with
safeFromData :: UnsafeFromData x => AsData x -> x
safeToData :: ToData x => x -> AsData x
unsafeInjectData :: BuiltinData -> AsData x
forgetData :: AsData x -> BuiltinData
inefficientMapData :: (UnsafeFromData x, ToData y) => (x -> y) -> AsData x -> AsData y
mkTypedValidator :: CompiledCode (AsData (DatumType a) -> AsData (RedeemerType a) -> AsData ScriptContext -> Bool)
                             -> CompiledCode (ValidatorType a -> WrappedValidatorType) -> TypedValidator a
  1. Provide a family of functions that map the AsData ScriptContext to its fields, and same for all the product types it contains. For example
scriptContextTxInfo :: AsData ScriptContext -> AsData TxInfo
txInfoInputs :: AsData TxInfo -> AsData [TxInInfo]

We should be able to generate all of these functions using either Generics.SOP or Template Haskell.

  1. Optionally mirror the collection of other functions that work with ScriptContext, such as
txSignedBy :: AsData TxInfo -> PubKeyHash -> Bool
findDatumHash :: Datum -> AsData TxInfo -> Maybe DatumHash
findOwnInput :: AsData ScriptContext -> Maybe (AsData TxInInfo)
@kozross
Copy link
Contributor

kozross commented Feb 9, 2022

If these functions need to work on-chain, generics-sop is a non-starter. TH is the only thing that will work really.

@samuelWilliams99
Copy link
Contributor

I really like this idea, would love to see it happen

@re-xyr
Copy link
Contributor

re-xyr commented Feb 13, 2022

How are end users expected to do arbitrary manipulation on AsData values? Is that through inefficientMapData?

@blamario
Copy link
Author

@re-xyr That is an option, but I was serious about the function name: it really would be inefficient to convert fromData, manipulate the record, and convert toData again. The proper way to use the library (if it is be a whole new library) would be to first narrow down AsData ScriptContext to only the field(s) you need, then to either convert them fromData using one of the functions from step #2, or to pass them directly to one of the functions from step #3.

@re-xyr
Copy link
Contributor

re-xyr commented Feb 20, 2022

If i understood correctly, the family of functions that extracts AsData ScriptContext fields or otherwise interact with them needs to directly manipulate Data representations. Is that correct?

@blamario
Copy link
Author

Yes, that's correct. It's very easy to write a function that type-checks, yet extracts a wrong field or something that's not even a field. That's why it would be best to generate all the field accessor functions automatically. Ideally they would be lenses and not just getters, but I fear that would be too much for Plutus to handle.

@re-xyr
Copy link
Contributor

re-xyr commented Feb 28, 2022

@blamario i've experimented a little. Questions so far:

  • How are users supposed to access sum types and primitive types?
  • How are users supposed to construct values?
  • This type:
    mkTypedValidator :: CompiledCode (AsData (DatumType a) -> AsData (RedeemerType a) -> AsData ScriptContext -> Bool)
                     -> CompiledCode (ValidatorType a -> WrappedValidatorType) -> TypedValidator a
    
    doesn't seem to be implementable. We can instead implement these two that can be used with mkTypedValidator and mkValidatorScript:
    toTypedValidator ::
      forall (d :: Type) (r :: Type).
      (ToData d, ToData r) =>
      (AsData d -> AsData r -> AsData ScriptContext -> Bool) ->
      (d -> r -> ScriptContext -> Bool)
    
    toWrappedValidator ::
      forall (d :: Type) (r :: Type).
      (AsData d -> AsData r -> AsData ScriptContext -> Bool) ->
      WrappedValidatorType
    
    are these satisfactory?

@re-xyr re-xyr linked a pull request Feb 28, 2022 that will close this issue
@blamario
Copy link
Author

blamario commented Feb 28, 2022

Good questions all.

* How are users supposed to access sum types and primitive types?

User-defined sum types will be tricky to support safely and efficiently at the same time, and I'd suggest leaving them off for now. As far as Haskell goes, it could be done using generics and constructor labels, or alternatively requiring higher-kinded data declarations. The problem is, I don't know if plutus-tx-plugin could handle these techniques. I'm guessing not. If there's no safe way that works, about the best we can provide is something like

-- | @unsafeFieldFromSum m n@ extracts the @n@th field, assuming the @m@th constructor.
unsafeFieldFromSum :: (UnsafeFromData x) => Int -> Int -> AsData x -> AsData y

For data types built into Plutus, we'd provide all field accessors by name and hide the ugliness.

* How are users supposed to construct values?

Again, if you're asking about user-defined types this would depend on how much plutus-tx-plugin can swallow. I can run an experiment to check.

* This type:
  ```
  mkTypedValidator :: CompiledCode (AsData (DatumType a) -> AsData (RedeemerType a) -> AsData ScriptContext -> Bool)
                   -> CompiledCode (ValidatorType a -> WrappedValidatorType) -> TypedValidator a
  ```
  doesn't seem to be implementable. 
   ...
  are these satisfactory?

Is the only problem that it's missing the (ToData (DatumType a), ToData (RedeemerType a)) constraints? I was hoping for a straightforward replacement for the existing mkTypedValidator function, but your alternative is not too difficult to plug in either.

@blamario
Copy link
Author

blamario commented Feb 28, 2022

On the second thought, we can definitely implement safe answers for your first two questions using Template Haskell. So if the user had

data MyRedeemer = SellAll | Move Value Address
$(genAsData ''MyRedeemer)

the splice could expand to

data MyRedeemerAsData = SelAllAsData | MoveAsData (AsData Value) (AsData Address)
myRedeemerFromData :: MyRedeemerAsData -> AsData MyRedeemer
myRedeemerToData :: AsData MyRedeemer -> MyRedeemerAsData

with the obvious definitions.

@blamario
Copy link
Author

Also, assuming plutus-tx-plugin doesn't object, we can provide a type class corresponding to and generate its instance instead of the three top-level declarations:

class RepresentedAsData a where
   type DataRep a
   fromDataRep :: DataRep a -> AsData a
   toDataRep :: AsData a -> DataRep a

@kozross
Copy link
Contributor

kozross commented Mar 21, 2022

@re-xyr @blamario - status on this? It'd be nice to get this functionality if we can, but I'm not sure where it's at currently.

@re-xyr
Copy link
Contributor

re-xyr commented Mar 22, 2022

@kozross #191 currently implements a prototype of 1) and 2) of the requested functionalities; I think we still need quite a bit of additions to get it merged into master.

@kozross
Copy link
Contributor

kozross commented Mar 22, 2022

@re-xyr OK, fair enough. No rush, unless there's some urgency to getting this in, @blamario?

@blamario
Copy link
Author

No there's no urgency.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants
@blamario @kozross @samuelWilliams99 @re-xyr and others