IntersectMBO / plutus

The Plutus language implementation and tools
Apache License 2.0
1.57k stars 480 forks source link

Is it possible to add a cheaper way to get hash from Plutus data? #6524

Closed zlonast closed 2 months ago

zlonast commented 2 months ago

Describe the feature you'd like

I would like to have a feature like this

dataToHash :: ToData a => a -> BuiltinByteString
dataToHash = ...

(I would also like the hash to fit into 28 bytes)

I think it's worth clarifying the context.

        info  = scriptContextTxInfo ctx

        ins   = txInfoInputs info
        refs  = txInfoReferenceInputs info
        outs  = txInfoOutputs info
        range = txInfoValidRange info

        hash = blake2b_224 . serialiseData . toBuiltinData $ (ins, refs, outs, range)

Describe alternatives you've considered

For now I use the following implementation

dataToBlake :: ToData a => a -> BuiltinByteString
dataToBlake = blake2b_224 . serialiseData . toBuiltinData
effectfully commented 2 months ago

For now I use the following implementation

And you are not satisfied with that and believe that it can be improved somehow? How? I think It's toBuiltinData that is the bottleneck here, so even if we create a builtin for something like blake2b_224 . serialiseData, I don't feel like it'll help much. And given that ToData is a Plinth type class, toBuiltinData is gonna stay expensive, unless we provide a way to serialize the AST of any Plutus value, which is not only gonna break parametricity for the entirety of Plutus (and parametricity is a hill that some of us are willing to die on, see #5440), but also is probably not gonna respect beta-equality, i.e. will basically wildly misbehave.

So I'm not sure what you're proposing here.

zlonast commented 2 months ago

Thanks for your the answer, I think I see what you're talking about.

So I'm not sure what you're proposing here.

I was looking for answers here on where to find performance for not very meaningful conversions to Data and Cborg.

but also is probably not gonna respect beta-equality, i.e. will basically wildly misbehave.

It seems to me that beta-equality is not very important in such cases and is not a very unsafe thing.

unless we provide a way to serialize the AST of any Plutus value, which is not only gonna break parametricity for the entirety of Plutus

Perhaps I don't fully understand why breaking parametricity is a problem for a user who wants to take responsibility.

Plutus Core is the low-level language, it's okay for people to have to know what they're doing.

Why do we need static types if we expect people to know what they're doing?

🤔

colll78 commented 2 months ago

I'm assuming the code you are interested in optimizing is from here

The vast majority of the budget from this contract is consumed by the conversion of the script context from and to data encoding.

symbolicVerifier :: SetupBytes -> ProofBytes -> ScriptContext -> Bool
symbolicVerifier contract proof ctx = ...
        info  = scriptContextTxInfo ctx
        ins   = txInfoInputs info
        refs  = txInfoReferenceInputs info
        outs  = txInfoOutputs info
        range = txInfoValidRange info
        hash = blake2b_224 . serialiseData . toBuiltinData $ (ins, refs, outs, range)

To

symbolicVerifier :: SetupBytes -> BuiltinData -> BuiltinUnit
symbolicVerifier contract ctx =
    -- Verifying the Plonk `proof` for the `contract` on the transaction data encoded as `input`
    verify @PlonkPlutus @HaskellCore contract input proof
    where
      ctxFields = BI.snd (BI.unsafeDataAsConstr ctx)
      info = BI.head ctxFields 

      -- Extract redeemer from ScriptContext
      proof = unsafeFromBuiltinData @SetupBytes (BI.head $ BI.tail ctxFields)

      -- Extracting transaction data
      infoFields  = BI.snd (BI.unsafeDataAsConstr (BI.head ctxFields))
      ins = BI.head infoFields
      info_before_ref_inputs = BI.tail txInfo
      refs = BI.head info_before_ref_inputs
      info_before_outputs = BI.tail info_before_ref_inputs
      outs = BI.head info_before_outputs
      range = BI.head BI.tail $ BI.tail $ BI.tail $ BI.tail $ BI.tail info_before_outputs

      -- Computing public input from the transaction data
      txData = BI.mkConstr ins $ BI.mkCons refs $ BI.mkCons outs $ BI.mkConstr range $ BI.mkNilData BI.unitval
      input = toInput . blake2b_224 . serialiseData $ txData 
zlonast commented 2 months ago

I'm assuming the code you are interested in optimizing is from here

The vast majority of the budget from this contract is consumed by the conversion of the script context from and to data encoding.

Thank you very much, I really missed this optimization path.