IntersectMBO / plutus-apps

The Plutus application platform
Apache License 2.0
306 stars 214 forks source link

`mustSatisfyAnyOf` not behaving as expected #438

Open DrXano opened 2 years ago

DrXano commented 2 years ago

Summary

The constraint mustSatisfyAnyOf is not behaving as i expected. I am working with the state machine API and in the transition function i want to allow only a specific set of Wallets to perform a transition.

{-# INLINABLE transition #-}
transition :: State CounterTriggerState -> CounterTriggerInput -> Maybe (TxConstraints Void Void, State CounterTriggerState)
transition State{stateData=oldData,stateValue} input = case (oldData, input) of

    (Step1State oldS1Id oldTriggerTimeStamps, Step2Input s1Id triggerTimeStamps stateValue)
        -> Just(validateKeys s1Id, State{stateData = Step2State s1Id triggerTimeStamps, stateValue = stateValue})

    (Step2State oldS1Id oldTriggerTimeStamps, Step3Input s1Id triggerTimeStamps stateValue)
        -> Just(validateKeys s1Id, State{stateData = Step3State s1Id triggerTimeStamps, stateValue = mempty})

    _ -> Nothing

s1Id in the code above is a list of the Public keys that are allowed to perform a specific transition, other keys besides the ones in s1Id are not allowed to perform it. And to ensure that, i wrote the validateKeys function:

validateKeys :: [PaymentPubKeyHash] -> TxConstraints Void Void
validateKeys keys = if null keys
    then mempty
    else Constraints.mustSatisfyAnyOf $ map (Constraints.mustBeSignedBy) keys

To make sure that a transition is being performed by the right wallet im using mustBeSignedBy. Now to see if it is being performed by one of the wallets in s1Id , i can't do mustBeSignedBy key1 <> mustBeSignedBy key2 <> mustBeSignedBy key3 <>... because it will require the signature of all those wallets. mustSatisfyAnyOf has no documentation, but by looking at the name i assume that given a list of constraints, if at least one of them is being satisfied, the constraint created by the function is satisfied as well.

And with that, if i perform a transition using the first key of the list all goes well, but doing the same transition with the second key of the list the validation fails.

Steps to reproduce the behavior

  1. Make a contract using the state machine API where the states are holding a list of PaymentPubKeyHash.

  2. In the transition function, set the constraints of one, or all the transitions, to Constraints.mustSatisfyAnyOf $ map (Constraints.mustBeSignedBy) keys or use the validateKeys i have shown in the summary. (keys represent a list of PaymentPubKeyHash).

  3. Set the keys of the wallets in the list that is in the state, two keys is enough. I made a function to convert a String representation of a key to the correct data type:

    stringToKey :: String -> PaymentPubKeyHash
    stringToKey key = PaymentPubKeyHash{unPaymentPubKeyHash = fromString key}
  4. Perform a transition using a wallet associated to the first key of the list. You will see the transition has been performed.

4.1. Perform that transition with a wallet whose key is not in the list, the validation will fail but that is supposed to happen since i want to allow only specific wallets.

  1. Perform the same transition but with the wallet associated with the second key of the list, the validation will fail.

Actual Result

Doing the transition with the first key of the list goes well but with the second key i get this:

Validation failed: 79048a56100bba09eb155852f88116f9e2521f2fe8a43c4510bf4d1198659cec
 (CardanoLedgerValidationError "ApplyTxError [UtxowFailure (MissingRequiredSigners (fromList [KeyHash \"a2c20c77887ace1cd986193e4e75babd8993cfd56995cd5cfce609c2\"]))]")

The a2c20c77887ace1cd986193e4e75babd8993cfd56995cd5cfce609c2 is the first key of the list.

Expected Result

Both keys should be able to perform the same transition with the help of mustSatisfyAnyOf.

Describe the approach you would take to fix this

I assume that mustSatisfyAnyOf is satisfied if at least on of the constraints given to the function is satisfied. But if i am wrong please document the function.

System info

I am pretty much using the online Playground

zmrocze commented 2 years ago

Were you using MustSatisfyAnyOf [TxConstraint] or MustSatisfyAnyOf [[TxConstraint]]?

It should be the second one and it means "Must satisfy ALL constraints from ONE of the constraint lists", in other words it's a sum of products.