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

Logical operations #5970

Merged
merged 22 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions plutus-core/changelog.d/20240510_104627_koz.ross_logical.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!--
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Removed

- A bullet item for the Removed category.

-->
### Added

- Logical operations as per [CIP-122](https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-0122/CIP-0122.md).

### Changed

- References to CIP-87 have been corrected to refer to CIP-121.

<!--
### Deprecated

- A bullet item for the Deprecated category.

-->
<!--
### Fixed

- A bullet item for the Fixed category.

-->
<!--
### Security

- A bullet item for the Security category.

-->
2 changes: 2 additions & 0 deletions plutus-core/plutus-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ library
PlutusCore.Annotation
PlutusCore.Arity
PlutusCore.Bitwise.Convert
PlutusCore.Bitwise.Logical
PlutusCore.Builtin
PlutusCore.Builtin.Debug
PlutusCore.Builtin.Elaborate
Expand Down Expand Up @@ -422,6 +423,7 @@ test-suite untyped-plutus-core-test
Evaluation.Builtins.Conversion
Evaluation.Builtins.Costing
Evaluation.Builtins.Definition
Evaluation.Builtins.Laws
Evaluation.Builtins.MakeRead
Evaluation.Builtins.SignatureVerification
Evaluation.Debug
Expand Down
4 changes: 2 additions & 2 deletions plutus-core/plutus-core/src/PlutusCore/Bitwise/Convert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ data IntegerToByteStringError =
deriving stock (Eq, Show)

-- | Conversion from 'Integer' to 'ByteString', as per
-- [CIP-0087](https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-XXXX).
-- [CIP-121](https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-0121).
--
-- For performance and clarity, the endianness argument uses
-- 'ByteOrder', and the length argument is an 'Int'.
Expand Down Expand Up @@ -232,7 +232,7 @@ integerToByteString requestedByteOrder requestedLength input
Builder.bytes (BS.replicate paddingLength 0x0) <> acc

-- | Conversion from 'ByteString' to 'Integer', as per
-- [CIP-0087](https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-XXXX).
-- [CIP-121](https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-0121).
--
-- For clarity, the stated endianness argument uses 'ByteOrder'.
byteStringToInteger :: ByteOrder -> ByteString -> Integer
Expand Down
464 changes: 464 additions & 0 deletions plutus-core/plutus-core/src/PlutusCore/Bitwise/Logical.hs
Copy link
Contributor

Choose a reason for hiding this comment

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

There's lots of impressively fiddly code in here. I'm glad we have all those tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the reason why I have all these tests. I got it wrong enough times when I first wrote this that I needed the tests to make sure something wasn't subtly broken all the time.

Large diffs are not rendered by default.

105 changes: 94 additions & 11 deletions plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import PlutusCore.Evaluation.Result (EvaluationResult (..))
import PlutusCore.Pretty (PrettyConfigPlc)

import PlutusCore.Bitwise.Convert as Convert
import PlutusCore.Bitwise.Logical as Logical
import PlutusCore.Crypto.BLS12_381.G1 qualified as BLS12_381.G1
import PlutusCore.Crypto.BLS12_381.G2 qualified as BLS12_381.G2
import PlutusCore.Crypto.BLS12_381.Pairing qualified as BLS12_381.Pairing
Expand Down Expand Up @@ -152,6 +153,14 @@ data DefaultFun
-- Conversions
| IntegerToByteString
| ByteStringToInteger
-- Logical
| AndByteString
| OrByteString
| XorByteString
| ComplementByteString
| ReadBit
| WriteBits
| ReplicateByteString
deriving stock (Show, Eq, Ord, Enum, Bounded, Generic, Ix)
deriving anyclass (NFData, Hashable, PrettyBy PrettyConfigPlc)

Expand Down Expand Up @@ -1805,21 +1814,80 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where
-- Conversions
{- See Note [Input length limitation for IntegerToByteString] -}
toBuiltinMeaning _semvar IntegerToByteString =
let integerToByteStringDenotation :: Bool -> LiteralByteSize -> Integer -> BuiltinResult BS.ByteString
{- The second argument is wrapped in a LiteralByteSize to allow us to interpret it as a size during
costing. It appears as an integer in UPLC: see Note [Integral types as Integer]. -}
integerToByteStringDenotation b (LiteralByteSize w) n = integerToByteStringWrapper b w n
{-# INLINE integerToByteStringDenotation #-}
in makeBuiltinMeaning
integerToByteStringDenotation
(runCostingFunThreeArguments . paramIntegerToByteString)
let integerToByteStringDenotation :: Bool -> LiteralByteSize -> Integer -> BuiltinResult BS.ByteString
{- The second argument is wrapped in a LiteralByteSize to allow us to interpret it as a size during
costing. It appears as an integer in UPLC: see Note [Integral types as Integer]. -}
integerToByteStringDenotation b (LiteralByteSize w) n = integerToByteStringWrapper b w n
{-# INLINE integerToByteStringDenotation #-}
in makeBuiltinMeaning
integerToByteStringDenotation
(runCostingFunThreeArguments . paramIntegerToByteString)

toBuiltinMeaning _semvar ByteStringToInteger =
let byteStringToIntegerDenotation :: Bool -> BS.ByteString -> Integer
byteStringToIntegerDenotation = byteStringToIntegerWrapper
{-# INLINE byteStringToIntegerDenotation #-}
let byteStringToIntegerDenotation :: Bool -> BS.ByteString -> Integer
byteStringToIntegerDenotation = byteStringToIntegerWrapper
{-# INLINE byteStringToIntegerDenotation #-}
in makeBuiltinMeaning
byteStringToIntegerDenotation
(runCostingFunTwoArguments . paramByteStringToInteger)

-- Logical
toBuiltinMeaning _semvar AndByteString =
let andByteStringDenotation :: Bool -> BS.ByteString -> BS.ByteString -> BS.ByteString
andByteStringDenotation = Logical.andByteString
{-# INLINE andByteStringDenotation #-}
in makeBuiltinMeaning
andByteStringDenotation
(runCostingFunThreeArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar OrByteString =
let orByteStringDenotation :: Bool -> BS.ByteString -> BS.ByteString -> BS.ByteString
orByteStringDenotation = Logical.orByteString
{-# INLINE orByteStringDenotation #-}
in makeBuiltinMeaning
orByteStringDenotation
(runCostingFunThreeArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar XorByteString =
let xorByteStringDenotation :: Bool -> BS.ByteString -> BS.ByteString -> BS.ByteString
xorByteStringDenotation = Logical.xorByteString
{-# INLINE xorByteStringDenotation #-}
in makeBuiltinMeaning
xorByteStringDenotation
(runCostingFunThreeArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar ComplementByteString =
let complementByteStringDenotation :: BS.ByteString -> BS.ByteString
complementByteStringDenotation = Logical.complementByteString
{-# INLINE complementByteStringDenotation #-}
in makeBuiltinMeaning
complementByteStringDenotation
(runCostingFunOneArgument . unimplementedCostingFun)

toBuiltinMeaning _semvar ReadBit =
let readBitDenotation :: BS.ByteString -> Int -> BuiltinResult Bool
readBitDenotation = Logical.readBit
{-# INLINE readBitDenotation #-}
in makeBuiltinMeaning
readBitDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar WriteBits =
let writeBitsDenotation :: BS.ByteString -> [(Integer, Bool)] -> BuiltinResult BS.ByteString
writeBitsDenotation = Logical.writeBits
{-# INLINE writeBitsDenotation #-}
in makeBuiltinMeaning
writeBitsDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

toBuiltinMeaning _semvar ReplicateByteString =
let byteStringReplicateDenotation :: Int -> Word8 -> BuiltinResult BS.ByteString
byteStringReplicateDenotation = Logical.replicateByteString
{-# INLINE byteStringReplicateDenotation #-}
in makeBuiltinMeaning
byteStringReplicateDenotation
(runCostingFunTwoArguments . unimplementedCostingFun)

-- See Note [Inlining meanings of builtins].
{-# INLINE toBuiltinMeaning #-}

Expand Down Expand Up @@ -1947,6 +2015,14 @@ instance Flat DefaultFun where
IntegerToByteString -> 73
ByteStringToInteger -> 74

AndByteString -> 75
OrByteString -> 76
XorByteString -> 77
ComplementByteString -> 78
ReadBit -> 79
WriteBits -> 80
ReplicateByteString -> 81

decode = go =<< decodeBuiltin
where go 0 = pure AddInteger
go 1 = pure SubtractInteger
Expand Down Expand Up @@ -2023,6 +2099,13 @@ instance Flat DefaultFun where
go 72 = pure Blake2b_224
go 73 = pure IntegerToByteString
go 74 = pure ByteStringToInteger
go 75 = pure AndByteString
go 76 = pure OrByteString
go 77 = pure XorByteString
go 78 = pure ComplementByteString
go 79 = pure ReadBit
go 80 = pure WriteBits
go 81 = pure ReplicateByteString
go t = fail $ "Failed to decode builtin tag, got: " ++ show t

size _ n = n + builtinTagWidth
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,12 @@ isCommutative = \case
MkNilPairData -> False
IntegerToByteString -> False
ByteStringToInteger -> False
-- Currently, this requires commutativity in all arguments, which the
-- logical operations are not.
AndByteString -> False
OrByteString -> False
XorByteString -> False
ComplementByteString -> False
ReadBit -> False
WriteBits -> False
ReplicateByteString -> False
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@ import Test.Tasty (TestTree)
import Test.Tasty.HUnit (assertEqual, assertFailure, testCase)
import Text.Show.Pretty (ppShow)

-- Properties and examples directly from CIP-0087:
-- Properties and examples directly from CIP-121:
--
-- - https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-XXXX#builtinintegertobytestring
-- - https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-XXXX#builtinbytestringtointeger

-- - https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-0121#builtinintegertobytestring
-- - https://github.com/mlabs-haskell/CIPs/tree/koz/to-from-bytestring/CIP-0121#builtinbytestringtointeger

-- lengthOfByteString (integerToByteString e d 0) = d
i2bProperty1 :: PropertyT IO ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import PlutusCore.Pretty
import PlutusPrelude
import UntypedPlutusCore.Evaluation.Machine.Cek

import PlutusCore qualified as PLC
import PlutusCore.Examples.Builtins
import PlutusCore.Examples.Data.Data
import PlutusCore.StdLib.Data.Bool
Expand All @@ -39,20 +40,19 @@ import PlutusCore.StdLib.Data.ScottList qualified as Scott
import PlutusCore.StdLib.Data.ScottUnit qualified as Scott
import PlutusCore.StdLib.Data.Unit

import Evaluation.Builtins.BLS12_381 (test_BLS12_381)
import Evaluation.Builtins.Common
import Evaluation.Builtins.Conversion qualified as Conversion
import Evaluation.Builtins.SignatureVerification (ecdsaSecp256k1Prop, ed25519_VariantAProp,
ed25519_VariantBProp, ed25519_VariantCProp,
schnorrSecp256k1Prop)


import Control.Exception
import Data.ByteString (ByteString, pack)
import Data.DList qualified as DList
import Data.Proxy
import Data.String (IsString (fromString))
import Data.Text (Text)
import Evaluation.Builtins.BLS12_381 (test_BLS12_381)
import Evaluation.Builtins.Common
import Evaluation.Builtins.Conversion qualified as Conversion
import Evaluation.Builtins.Laws qualified as Laws
import Evaluation.Builtins.SignatureVerification (ecdsaSecp256k1Prop, ed25519_VariantAProp,
ed25519_VariantBProp, ed25519_VariantCProp,
schnorrSecp256k1Prop)
import Hedgehog hiding (Opaque, Size, Var)
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
Expand Down Expand Up @@ -889,7 +889,7 @@ test_Conversion =
-- appendByteString (integerToByteString False 0 q)
-- (integerToByteString False 0 r)
testPropertyNamed "property 7" "i2b_prop7" . property $ Conversion.i2bProperty7,
testGroup "CIP-0087 examples" Conversion.i2bCipExamples,
testGroup "CIP-121 examples" Conversion.i2bCipExamples,
testGroup "Tests for integerToByteString size limit" Conversion.i2bLimitTests
],
testGroup "ByteString -> Integer" [
Expand All @@ -899,10 +899,56 @@ test_Conversion =
testPropertyNamed "property 2" "b2i_prop2" . property $ Conversion.b2iProperty2,
-- integerToByteString b (lengthOfByteString bs) (byteStringToInteger b bs) = bs
testPropertyNamed "property 3" "b2i_prop3" . property $ Conversion.b2iProperty3,
testGroup "CIP-0087 examples" Conversion.b2iCipExamples
testGroup "CIP-121 examples" Conversion.b2iCipExamples
]
]

-- Tests for the logical operations, as per [CIP-122](https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-0122/CIP-0122.md)
test_Logical :: TestTree
test_Logical =
adjustOption (\x -> max x . HedgehogTestLimit . Just $ 4000) .
testGroup "Logical" $ [
testGroup "andByteString" [
Laws.abelianSemigroupLaws "truncation" PLC.AndByteString False,
Laws.idempotenceLaw "truncation" PLC.AndByteString False,
Laws.absorbtionLaw "truncation" PLC.AndByteString False "",
Laws.leftDistributiveLaw "truncation" "itself" PLC.AndByteString PLC.AndByteString False,
Laws.leftDistributiveLaw "truncation" "OR" PLC.AndByteString PLC.OrByteString False,
Laws.leftDistributiveLaw "truncation" "XOR" PLC.AndByteString PLC.XorByteString False,
Laws.abelianMonoidLaws "padding" PLC.AndByteString True "",
Laws.distributiveLaws "padding" PLC.AndByteString True
],
testGroup "orByteString" [
Laws.abelianSemigroupLaws "truncation" PLC.OrByteString False,
Laws.idempotenceLaw "truncation" PLC.OrByteString False,
Laws.absorbtionLaw "truncation" PLC.OrByteString False "",
Laws.leftDistributiveLaw "truncation" "itself" PLC.OrByteString PLC.OrByteString False,
Laws.leftDistributiveLaw "truncation" "AND" PLC.OrByteString PLC.AndByteString False,
Laws.abelianMonoidLaws "padding" PLC.OrByteString True "",
Laws.distributiveLaws "padding" PLC.OrByteString True
],
testGroup "xorByteString" [
Laws.abelianSemigroupLaws "truncation" PLC.XorByteString False,
Laws.absorbtionLaw "truncation" PLC.XorByteString False "",
Laws.xorInvoluteLaw,
Laws.abelianMonoidLaws "padding" PLC.XorByteString True ""
],
testGroup "bitwiseLogicalComplement" [
Laws.complementSelfInverse,
Laws.deMorgan
],
testGroup "bit reading and modification" [
Laws.getSet,
Laws.setGet,
Laws.setSet,
Laws.writeBitsHomomorphismLaws
],
testGroup "replicateByteString" [
Laws.replicateHomomorphismLaws,
Laws.replicateIndex
]
]

test_definition :: TestTree
test_definition =
testGroup "definition"
Expand Down Expand Up @@ -938,4 +984,5 @@ test_definition =
, test_Version
, test_ConsByteString
, test_Conversion
, test_Logical
]
Loading