Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support reference scripts #4070

Closed

Conversation

paweljakubas
Copy link
Contributor

  • I have ...

Comments

Issue Number

Copy link
Member

@jonathanknowles jonathanknowles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @paweljakubas

Some early comments!

I propose to revise our generators of arbitrary TxOut values so that we're not always generating values of Nothing for the script field. I suspect that if we do this, that several property tests will fail. (See comments.)

@@ -57,6 +63,9 @@ import GHC.Generics
import qualified Cardano.Wallet.Primitive.Types.Coin as Coin
import qualified Cardano.Wallet.Primitive.Types.TokenBundle as TokenBundle
import qualified Cardano.Wallet.Primitive.Types.TokenMap as TokenMap
import qualified Data.Text as T
import qualified Text.ParserCombinators.ReadP as Parse

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

@@ -84,7 +95,7 @@ data TxOut = TxOut
instance Ord TxOut where
compare = comparing projection
where
projection (TxOut address bundle) = (address, Lexicographic bundle)
projection (TxOut address bundle _) = (address, Lexicographic bundle)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @paweljakubas

I think this Ord instance is not lawful w.r.t. the antisymmetry law:

  • (x <= y && y <= x) ==> (x == y)

Here's a counterexample:

>>> s1 == s2
False
>>> TxOut a b s1 <= TxOut a b s2
True
>>> TxOut a b s1 >= TxOut a b s2
True
>>> TxOut a b s1 == TxOut a b s2
False 💥

To fix this, we need to take all parts of a TxOut into account:

Suggested change
projection (TxOut address bundle _) = (address, Lexicographic bundle)
projection (TxOut address bundle script) =
( address
, Lexicographic bundle
, script
)

Comment on lines +129 to +142
scriptToText :: Script KeyHash -> T.Text
scriptToText (RequireSignatureOf keyhash) =
keyHashToText keyhash
scriptToText (RequireAllOf contents) =
"all [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (RequireAnyOf contents) =
"any [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (RequireSomeOf m contents) =
"at_least "<> T.pack (show m) <>
" [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (ActiveFromSlot s) =
"active_from " <> T.pack (show s)
scriptToText (ActiveUntilSlot s) =
"active_until " <> T.pack (show s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
scriptToText :: Script KeyHash -> T.Text
scriptToText (RequireSignatureOf keyhash) =
keyHashToText keyhash
scriptToText (RequireAllOf contents) =
"all [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (RequireAnyOf contents) =
"any [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (RequireSomeOf m contents) =
"at_least "<> T.pack (show m) <>
" [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (ActiveFromSlot s) =
"active_from " <> T.pack (show s)
scriptToText (ActiveUntilSlot s) =
"active_until " <> T.pack (show s)
scriptToText :: Script KeyHash -> T.Text
scriptToText (RequireSignatureOf keyhash) =
keyHashToText keyhash
scriptToText (RequireAllOf contents) =
"all [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (RequireAnyOf contents) =
"any [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (RequireSomeOf m contents) =
"at_least "<> T.pack (show m) <>
" [" <> T.intercalate "," (map scriptToText contents) <> "]"
scriptToText (ActiveFromSlot s) =
"active_from " <> T.pack (show s)
scriptToText (ActiveUntilSlot s) =
"active_until " <> T.pack (show s)


toInternalUTxO' :: (TokenBundle -> b) -> (TxIn, TxOut) -> (WalletUTxO, b)
toInternalUTxO' f (i, TxOut a b) = (WalletUTxO i a, f b)
toInternalUTxO' f (i, TxOut a b _) = (WalletUTxO i a, f b)
Copy link
Member

@jonathanknowles jonathanknowles Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this will break the following round-trip property:

prop_toInternalUTxO_toExternalUTxO :: TxIn -> TxOut -> Property
prop_toInternalUTxO_toExternalUTxO i o =
(toExternalUTxO . toInternalUTxO) (i, o) === (i, o)

However, our current property tests will not discover this breakage, as this PR adjusts the generators for TxOut so that they always generate Nothing for the script field.

We should definitely adjust the generator for TxOut used by Cardano.Tx.Balance.Internal.CoinSelectionSpec so that it generates a wider range of values for the script field: i.e., not just Nothing.

To fix this round-trip property, I'm guessing we will probably need to add a script field to the WalletUTxO type (above).

@@ -123,7 +123,7 @@ genSelection = Selection
genAssetsToBurn = genTokenMap
genExtraCoinSource = genCoin
genExtraCoinSink = genCoin
genTxOutCoin = TxOut <$> genAddress <*> (TokenBundle.fromCoin <$> genCoin)
genTxOutCoin = TxOut <$> genAddress <*> (TokenBundle.fromCoin <$> genCoin) <*> pure Nothing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should generate a wider range of values here, and not just Nothing. In particular, always specifying Nothing hides the fact that this property no longer holds:

prop_toInternalUTxO_toExternalUTxO :: TxIn -> TxOut -> Property
prop_toInternalUTxO_toExternalUTxO i o =
(toExternalUTxO . toInternalUTxO) (i, o) === (i, o)

instance {-# OVERLAPPING #-} Show (Maybe (Script KeyHash)) where
show scriptM = case scriptM of
Nothing -> "not present"
Just script -> T.unpack . scriptToText $ script
Copy link
Member

@jonathanknowles jonathanknowles Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Is it actually necessary to define an overlapping instance here? 🤔

Could we instead define a Show instance for Script KeyHash (in the module that defines Script), and then just rely upon the default Show instance for Maybe?

@@ -54,11 +54,16 @@ genTxOut :: Gen TxOut
genTxOut = TxOut
<$> genAddress
<*> genTokenBundleSmallRange `suchThat` tokenBundleHasNonZeroCoin
<*> pure Nothing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should generate a wider range of values here than just Nothing.

Are there already generators for the Script type, and if so, can we import one of them and use it here?


shrinkTxOut :: TxOut -> [TxOut]
shrinkTxOut (TxOut a b) = uncurry TxOut <$> shrinkInterleaved
shrinkTxOut (TxOut a b c) =
map enrichPair $
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
map enrichPair $
enrichPair <$>

Comment on lines +345 to +346
Scripts.RequireSignature $
Ledger.KeyHash $ Crypto.UnsafeHash $ toShort h
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Scripts.RequireSignature $
Ledger.KeyHash $ Crypto.UnsafeHash $ toShort h
Scripts.RequireSignature $
Ledger.KeyHash $ Crypto.UnsafeHash $ toShort h

reference_policy_script_template:
<<: *ScriptTemplateValue
description: |
Optional policy script template that could be used as script reference
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Optional policy script template that could be used as script reference
Optional policy script template that could be used as a script reference

@@ -67,6 +76,8 @@ data TxOut = TxOut
:: !Address
, tokens
:: !TokenBundle
, script
:: !(Maybe (Script KeyHash))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can use the StrictMaybe type here instead?

@HeinrichApfelmus
Copy link
Contributor

I propose to revise our generators of arbitrary TxOut values so that we're not always generating values of Nothing for the script field. I suspect that if we do this, that several property tests will fail. (See comments.)

Actually, I asked Pawel to not change the TxOut type. 😅 We want to get rid of the TxOut it — and use a type from the Write hierarchy for this feature instead.

@paweljakubas paweljakubas self-assigned this Aug 9, 2023
@paweljakubas
Copy link
Contributor Author

closing in favor of #4086

@paweljakubas paweljakubas deleted the paweljakubas/adp-3090/support-reference-scripts branch August 22, 2023 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants