From aa989d5a0149312d5d10525a5fa19bbd5953eaca Mon Sep 17 00:00:00 2001 From: Yuriy Lazaryev Date: Wed, 15 Jul 2026 11:55:30 +0200 Subject: [PATCH 1/6] Expose multiIndexArray to Plinth (CIP-0156) Adds the PlutusTx.Builtins.multiIndexArray wrapper (mirroring indexArray: out-of-range indices fail, results in index-list order with duplicates preserved) and maps it to the MultiIndexArray builtin in the plugin, replacing the no-op arm. Golden tests cover the compiled PIR, UPLC, and CEK evaluation under GHC 9.6 and 9.12. Issue: IntersectMBO/plutus-private#2265 --- ...yev_issue_2265_multi_index_array_plinth.md | 4 +++ .../src/PlutusTx/Compiler/Builtins.hs | 4 +-- .../9.12/compiledMultiIndexArray.golden.eval | 1 + .../9.12/compiledMultiIndexArray.golden.pir | 33 +++++++++++++++++++ .../9.12/compiledMultiIndexArray.golden.uplc | 23 +++++++++++++ .../9.6/compiledMultiIndexArray.golden.eval | 1 + .../9.6/compiledMultiIndexArray.golden.pir | 33 +++++++++++++++++++ .../9.6/compiledMultiIndexArray.golden.uplc | 23 +++++++++++++ plutus-tx-plugin/test/Array/Spec.hs | 14 ++++++++ ...yev_issue_2265_multi_index_array_plinth.md | 5 +++ plutus-tx/src/PlutusTx/Builtins.hs | 1 + plutus-tx/src/PlutusTx/Builtins/Internal.hs | 7 ++++ 12 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 plutus-tx-plugin/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md create mode 100644 plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.eval create mode 100644 plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir create mode 100644 plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc create mode 100644 plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.eval create mode 100644 plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir create mode 100644 plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc create mode 100644 plutus-tx/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md diff --git a/plutus-tx-plugin/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md b/plutus-tx-plugin/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md new file mode 100644 index 00000000000..168ec7094cb --- /dev/null +++ b/plutus-tx-plugin/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md @@ -0,0 +1,4 @@ +### Added + +- Compilation support for the `multiIndexArray` builtin + ([CIP-0156](https://cips.cardano.org/cip/CIP-0156)). diff --git a/plutus-tx-plugin/src/PlutusTx/Compiler/Builtins.hs b/plutus-tx-plugin/src/PlutusTx/Compiler/Builtins.hs index 7ce5223eb01..9a456a3708c 100644 --- a/plutus-tx-plugin/src/PlutusTx/Compiler/Builtins.hs +++ b/plutus-tx-plugin/src/PlutusTx/Compiler/Builtins.hs @@ -234,6 +234,7 @@ builtinNames = , 'Builtins.lengthOfArray , 'Builtins.listToArray , 'Builtins.indexArray + , 'Builtins.multiIndexArray , ''Builtins.BuiltinData , 'Builtins.chooseData , 'Builtins.equalsData @@ -753,8 +754,7 @@ defineBuiltinTerms = do PLC.LengthOfArray -> defineBuiltinInl 'Builtins.lengthOfArray PLC.ListToArray -> defineBuiltinInl 'Builtins.listToArray PLC.IndexArray -> defineBuiltinInl 'Builtins.indexArray - -- Not exposed to Plinth yet: core-language only for now (CIP-0156). - PLC.MultiIndexArray -> pure () + PLC.MultiIndexArray -> defineBuiltinInl 'Builtins.multiIndexArray -- Data PLC.ChooseData -> defineBuiltinInl 'Builtins.chooseData PLC.EqualsData -> defineBuiltinInl 'Builtins.equalsData diff --git a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.eval b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.eval new file mode 100644 index 00000000000..3eed8238179 --- /dev/null +++ b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.eval @@ -0,0 +1 @@ +[I 3, I 1, I 1, I 2] \ No newline at end of file diff --git a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir new file mode 100644 index 00000000000..fd5fec816a3 --- /dev/null +++ b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir @@ -0,0 +1,33 @@ +(let + !multiIndexArray : all a. list integer -> array a -> list a + = multiIndexArray + in + multiIndexArray {data}) + (let + !mkCons : all a. a -> list a -> list a = mkCons + in + mkCons + {integer} + 2 + (mkCons {integer} 0 (mkCons {integer} 0 (mkCons {integer} 1 [])))) + (let + !unitval : unit = () + in + let + !mkNilData : unit -> list data = mkNilData + in + let + !mkI : integer -> data = iData + in + let + !mkCons : all a. a -> list a -> list a = mkCons + in + let + !listToArray : all a. list a -> array a = listToArray + in + listToArray + {data} + (mkCons + {data} + (mkI 1) + (mkCons {data} (mkI 2) (mkCons {data} (mkI 3) (mkNilData unitval))))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc new file mode 100644 index 00000000000..2741d204a36 --- /dev/null +++ b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc @@ -0,0 +1,23 @@ +(program + 1.1.0 + ((\multiIndexArray -> force multiIndexArray) + multiIndexArray + ((\mkCons -> + force mkCons 2 (force mkCons 0 (force mkCons 0 (force mkCons 1 [])))) + mkCons) + ((\unitval -> + (\mkNilData -> + (\mkI -> + (\mkCons -> + (\listToArray -> + force listToArray + (force mkCons + (mkI 1) + (force mkCons + (mkI 2) + (force mkCons (mkI 3) (mkNilData unitval))))) + listToArray) + mkCons) + iData) + mkNilData) + ()))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.eval b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.eval new file mode 100644 index 00000000000..3eed8238179 --- /dev/null +++ b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.eval @@ -0,0 +1 @@ +[I 3, I 1, I 1, I 2] \ No newline at end of file diff --git a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir new file mode 100644 index 00000000000..fd5fec816a3 --- /dev/null +++ b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir @@ -0,0 +1,33 @@ +(let + !multiIndexArray : all a. list integer -> array a -> list a + = multiIndexArray + in + multiIndexArray {data}) + (let + !mkCons : all a. a -> list a -> list a = mkCons + in + mkCons + {integer} + 2 + (mkCons {integer} 0 (mkCons {integer} 0 (mkCons {integer} 1 [])))) + (let + !unitval : unit = () + in + let + !mkNilData : unit -> list data = mkNilData + in + let + !mkI : integer -> data = iData + in + let + !mkCons : all a. a -> list a -> list a = mkCons + in + let + !listToArray : all a. list a -> array a = listToArray + in + listToArray + {data} + (mkCons + {data} + (mkI 1) + (mkCons {data} (mkI 2) (mkCons {data} (mkI 3) (mkNilData unitval))))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc new file mode 100644 index 00000000000..2741d204a36 --- /dev/null +++ b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc @@ -0,0 +1,23 @@ +(program + 1.1.0 + ((\multiIndexArray -> force multiIndexArray) + multiIndexArray + ((\mkCons -> + force mkCons 2 (force mkCons 0 (force mkCons 0 (force mkCons 1 [])))) + mkCons) + ((\unitval -> + (\mkNilData -> + (\mkI -> + (\mkCons -> + (\listToArray -> + force listToArray + (force mkCons + (mkI 1) + (force mkCons + (mkI 2) + (force mkCons (mkI 3) (mkNilData unitval))))) + listToArray) + mkCons) + iData) + mkNilData) + ()))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Array/Spec.hs b/plutus-tx-plugin/test/Array/Spec.hs index 4230c07cc25..0e55c5d50fd 100644 --- a/plutus-tx-plugin/test/Array/Spec.hs +++ b/plutus-tx-plugin/test/Array/Spec.hs @@ -15,6 +15,7 @@ module Array.Spec where import PlutusCore.Test (goldenUEval) import PlutusTx +import PlutusTx.Builtins (mkNil) import PlutusTx.Builtins.Internal import PlutusTx.Test (goldenPirReadable, goldenUPlcReadable) import Test.Tasty.Extras @@ -33,6 +34,9 @@ smokeTests = , goldenPirReadable "compiledIndexArray" compiledIndexArray , goldenUPlcReadable "compiledIndexArray" compiledIndexArray , goldenUEval "compiledIndexArray" [compiledIndexArray] + , goldenPirReadable "compiledMultiIndexArray" compiledMultiIndexArray + , goldenUPlcReadable "compiledMultiIndexArray" compiledMultiIndexArray + , goldenUEval "compiledMultiIndexArray" [compiledMultiIndexArray] ] ] @@ -63,3 +67,13 @@ compiledIndexArray = $$(compile [||indexArray||]) `unsafeApplyCode` compiledListToArray `unsafeApplyCode` liftCodeDef 2 + +compiledMultiIndexArray :: CompiledCode (BuiltinList BuiltinData) +compiledMultiIndexArray = + $$(compile [||multiIndexArray||]) + `unsafeApplyCode` compiledIndices + `unsafeApplyCode` compiledListToArray + +compiledIndices :: CompiledCode (BuiltinList BuiltinInteger) +compiledIndices = + $$(compile [||mkCons 2 (mkCons 0 (mkCons 0 (mkCons 1 mkNil)))||]) diff --git a/plutus-tx/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md b/plutus-tx/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md new file mode 100644 index 00000000000..62f593ff346 --- /dev/null +++ b/plutus-tx/changelog.d/20260715_113448_yuriy.lazaryev_issue_2265_multi_index_array_plinth.md @@ -0,0 +1,5 @@ +### Added + +- `PlutusTx.Builtins.multiIndexArray`: the `multiIndexArray` builtin from + [CIP-0156](https://cips.cardano.org/cip/CIP-0156), returning the array elements at the given + indices and failing on any out-of-bounds index. diff --git a/plutus-tx/src/PlutusTx/Builtins.hs b/plutus-tx/src/PlutusTx/Builtins.hs index 5750210b7b4..9f03dd11fe7 100644 --- a/plutus-tx/src/PlutusTx/Builtins.hs +++ b/plutus-tx/src/PlutusTx/Builtins.hs @@ -100,6 +100,7 @@ module PlutusTx.Builtins , sopListToArray , BI.lengthOfArray , BI.indexArray + , BI.multiIndexArray -- * Tracing , trace diff --git a/plutus-tx/src/PlutusTx/Builtins/Internal.hs b/plutus-tx/src/PlutusTx/Builtins/Internal.hs index b49a7397791..b1c0cc19996 100644 --- a/plutus-tx/src/PlutusTx/Builtins/Internal.hs +++ b/plutus-tx/src/PlutusTx/Builtins/Internal.hs @@ -688,6 +688,13 @@ indexArray (BuiltinArray v) i | otherwise = Haskell.error "array index out of bounds" {-# OPAQUE indexArray #-} +{-| Returns the elements at the given indices, in index-list order with duplicates preserved. + Fails if any index is not in the range @[0..j)@, where @j@ is the length of the array. -} +multiIndexArray :: BuiltinList BuiltinInteger -> BuiltinArray a -> BuiltinList a +multiIndexArray (BuiltinList is) (BuiltinArray v) = + BuiltinList (map (\i -> v Vector.! fromInteger i) is) +{-# OPAQUE multiIndexArray #-} + {- BLS12_381 -} From b3a2997c05e268842af43db2b1d8032186b897a0 Mon Sep 17 00:00:00 2001 From: Yuriy Lazaryev Date: Wed, 15 Jul 2026 15:20:34 +0200 Subject: [PATCH 2/6] Check all indices eagerly in the Integer domain in the multiIndexArray wrapper The Haskell simulation now matches the builtin: an index exceeding maxBound::Int is out-of-bounds instead of wrapping on conversion, and any out-of-bounds index fails the whole call regardless of how much of the result list is demanded. Issue: IntersectMBO/plutus-private#2265 --- plutus-tx/src/PlutusTx/Builtins/Internal.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plutus-tx/src/PlutusTx/Builtins/Internal.hs b/plutus-tx/src/PlutusTx/Builtins/Internal.hs index b1c0cc19996..16c086444b4 100644 --- a/plutus-tx/src/PlutusTx/Builtins/Internal.hs +++ b/plutus-tx/src/PlutusTx/Builtins/Internal.hs @@ -692,7 +692,17 @@ indexArray (BuiltinArray v) i Fails if any index is not in the range @[0..j)@, where @j@ is the length of the array. -} multiIndexArray :: BuiltinList BuiltinInteger -> BuiltinArray a -> BuiltinList a multiIndexArray (BuiltinList is) (BuiltinArray v) = - BuiltinList (map (\i -> v Vector.! fromInteger i) is) + -- All indices are checked eagerly in the 'Integer' domain, matching the builtin: an index + -- exceeding @maxBound :: Int@ is out-of-bounds rather than wrapping on conversion, and any + -- out-of-bounds index fails the whole call even if the result list is never fully demanded. + case traverse lookupIndex is of + Just els -> BuiltinList els + Nothing -> Haskell.error "array index out of bounds" + where + len = toInteger (Vector.length v) + lookupIndex i + | 0 <= i && i < len = Just (Vector.unsafeIndex v (fromInteger i)) + | otherwise = Nothing {-# OPAQUE multiIndexArray #-} {- From 6db16606f8fd1ed639aff1d113e7a393ad12ae0b Mon Sep 17 00:00:00 2001 From: Yuriy Lazaryev Date: Thu, 16 Jul 2026 12:52:13 +0200 Subject: [PATCH 3/6] Add regression tests for the multiIndexArray wrapper Pins the Haskell definition to the builtin semantics: index-list order with duplicates, eager Integer-domain bounds checking (an index exceeding maxBound::Int fails rather than wrapping), and whole-call failure regardless of how much of the result is demanded. Issue: IntersectMBO/plutus-private#2265 --- plutus-tx/plutus-tx.cabal | 1 + plutus-tx/test/Array/Spec.hs | 44 ++++++++++++++++++++++++++++++++++++ plutus-tx/test/Spec.hs | 4 +++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 plutus-tx/test/Array/Spec.hs diff --git a/plutus-tx/plutus-tx.cabal b/plutus-tx/plutus-tx.cabal index c581391ebcf..49204031e69 100644 --- a/plutus-tx/plutus-tx.cabal +++ b/plutus-tx/plutus-tx.cabal @@ -216,6 +216,7 @@ test-suite plutus-tx-test main-is: Spec.hs ghc-options: -threaded -rtsopts -with-rtsopts=-N other-modules: + Array.Spec Blueprint.Definition.Fixture Blueprint.Definition.Spec Blueprint.Spec diff --git a/plutus-tx/test/Array/Spec.hs b/plutus-tx/test/Array/Spec.hs new file mode 100644 index 00000000000..86c8fa0dfaf --- /dev/null +++ b/plutus-tx/test/Array/Spec.hs @@ -0,0 +1,44 @@ +{-# LANGUAGE TypeApplications #-} + +module Array.Spec (arrayTests) where + +import Control.Exception (ErrorCall) +import Data.Either (isLeft) +import PlutusCore.Test (pureTry) +import PlutusTx.Builtins.Internal qualified as BI + +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (Assertion, assertBool, testCase, (@?=)) +import Prelude + +-- The wrapper must fail exactly where the on-chain builtin fails: any index outside +-- @[0..length)@, including one exceeding @maxBound :: Int@, is an error rather than +-- a wrap-around lookup. +arrayTests :: TestTree +arrayTests = + testGroup + "Array" + [ testGroup + "multiIndexArray" + [ testCase "in-order with duplicates" $ + unList (BI.multiIndexArray (BI.BuiltinList [2, 0, 0, 1]) arr) @?= [30, 10, 10, 20] + , testCase "index exceeding maxBound::Int fails" $ + throwsErrorCall (BI.multiIndexArray (BI.BuiltinList [2 ^ (64 :: Int) + 2]) arr) + , testCase "out-of-bounds index fails even if the result is not fully demanded" $ + throwsErrorCall + ( case unList (BI.multiIndexArray (BI.BuiltinList [0, 3]) arr) of + x : _ -> x + [] -> 0 + ) + ] + ] + where + arr :: BI.BuiltinArray BI.BuiltinInteger + arr = BI.listToArray (BI.BuiltinList [10, 20, 30]) + + unList :: BI.BuiltinList a -> [a] + unList (BI.BuiltinList xs) = xs + + throwsErrorCall :: a -> Assertion + throwsErrorCall x = + assertBool "expected the evaluation to fail" (isLeft (pureTry @ErrorCall x)) diff --git a/plutus-tx/test/Spec.hs b/plutus-tx/test/Spec.hs index ce6a29b81b3..379bca11364 100644 --- a/plutus-tx/test/Spec.hs +++ b/plutus-tx/test/Spec.hs @@ -5,6 +5,7 @@ module Main (main) where +import Array.Spec (arrayTests) import Blueprint.Definition.Spec qualified import Bool.Spec (boolTests) import Builtins.Spec (builtinsTests) @@ -41,7 +42,8 @@ tests :: TestTree tests = testGroup "plutus-tx" - [ serdeTests + [ arrayTests + , serdeTests , sqrtTests , ratioTests , bytestringTests From 2950d16c98b72c1ab71db4bf66448b45eae474b2 Mon Sep 17 00:00:00 2001 From: Yuriy Lazaryev Date: Thu, 16 Jul 2026 12:52:33 +0200 Subject: [PATCH 4/6] Regenerate goldens shifted by the plugin builtinNames change Adding a name to the plugin's builtin table shifts unique allocation, which changes CSE tie-breaking in the readable-UPLC goldens of the two largest compiled programs; budgets and sizes are unchanged. Issue: IntersectMBO/plutus-private#2265 --- .../Data/GoldenTests/unsorted.golden.uplc | 78 +++++++-------- .../GoldenTests/unsorted.golden.uplc | 98 +++++++++---------- .../bitwise/test/9.6/Ed25519.golden.uplc | 4 +- 3 files changed, 90 insertions(+), 90 deletions(-) diff --git a/cardano-constitution/test/Cardano/Constitution/Validator/Data/GoldenTests/unsorted.golden.uplc b/cardano-constitution/test/Cardano/Constitution/Validator/Data/GoldenTests/unsorted.golden.uplc index bef92027c38..b761f20018e 100644 --- a/cardano-constitution/test/Cardano/Constitution/Validator/Data/GoldenTests/unsorted.golden.uplc +++ b/cardano-constitution/test/Cardano/Constitution/Validator/Data/GoldenTests/unsorted.golden.uplc @@ -741,19 +741,18 @@ program (constr 3 [ (constr 1 [ cse - , cse ]) ])) + , (constr 1 + [ (constr 0 + [ (constr 0 + [ ]) + , (constr 1 + [ cse + , cse ]) ]) + , (constr 0 + [ ]) ]) ]) ])) (constr 1 - [ (constr 0 - [ (constr 0 - [ ]) - , (constr 1 - [ cse - , (constr 1 - [ (unsafeRatio - 9 - 10) - , (constr 0 - [ ]) ]) ]) ]) + [ (cse + 4) , (constr 0 [ ]) ])) (constr 3 @@ -776,33 +775,34 @@ program (constr 3 [ (constr 1 [ cse - , (constr 1 - [ (constr 0 - [ (constr 0 - [ ]) - , (constr 1 - [ cse - , cse ]) ]) - , (constr 0 - [ ]) ]) ]) ])) - (constr 1 - [ (cse - 4) - , (constr 0 - [ ]) ])) - (constr 0 - [ (constr 1 - [ ]) - , (constr 1 - [ cse - , (constr 1 - [ (unsafeRatio - 51 - 100) - , (constr 0 - [ ]) ]) ]) ])) - (cse - 2)) ]) + , cse ]) ])) + (constr 0 + [ (constr 1 + [ ]) + , (constr 1 + [ cse + , (constr 1 + [ (unsafeRatio + 51 + 100) + , (constr 0 + [ ]) ]) ]) ])) + (cse + 2)) + (constr 1 + [ (constr 0 + [ (constr 0 + [ ]) + , (constr 1 + [ cse + , (constr 1 + [ (unsafeRatio + 9 + 10) + , (constr 0 + [ ]) ]) ]) ]) + , (constr 0 + [ ]) ])) ]) (constr 0 [ (constr 1 [ ]) diff --git a/cardano-constitution/test/Cardano/Constitution/Validator/GoldenTests/unsorted.golden.uplc b/cardano-constitution/test/Cardano/Constitution/Validator/GoldenTests/unsorted.golden.uplc index 900636e10dd..e1b5e4930fa 100644 --- a/cardano-constitution/test/Cardano/Constitution/Validator/GoldenTests/unsorted.golden.uplc +++ b/cardano-constitution/test/Cardano/Constitution/Validator/GoldenTests/unsorted.golden.uplc @@ -742,62 +742,62 @@ program [ ]) , (constr 1 [ cse - , cse ]) ]) + , (constr 1 + [ (unsafeRatio + 4 + 5) + , (constr 0 + [ ]) ]) ]) ]) , (constr 0 [ ]) ]) ]) ])) - (constr 1 - [ (cse - 4) - , (constr 0 - [ ]) ])) - (constr 3 - [ (constr 1 - [ cse - , cse ]) ])) - (constr 1 - [ (constr 0 - [ (constr 0 - [ ]) - , (constr 1 - [ cse - , (constr 1 - [ (unsafeRatio - 9 - 10) - , (constr 0 - [ ]) ]) ]) ]) - , (constr 0 - [ ]) ])) - (constr 3 + (constr 3 + [ (constr 1 + [ cse + , (constr 1 + [ (constr 0 + [ (constr 0 + [ ]) + , (constr 1 + [ cse + , cse ]) ]) + , (constr 0 + [ ]) ]) ]) ])) + (constr 1 + [ (cse + 4) + , (constr 0 + [ ]) ])) + (constr 3 + [ (constr 1 + [ cse + , cse ]) ])) + (constr 0 [ (constr 1 + [ ]) + , (constr 1 [ cse , (constr 1 - [ (constr 0 - [ (constr 0 - [ ]) - , (constr 1 - [ cse - , (constr 1 - [ (unsafeRatio - 4 - 5) - , (constr 0 - [ ]) ]) ]) ]) + [ (unsafeRatio + 51 + 100) , (constr 0 [ ]) ]) ]) ])) - (constr 0 - [ (constr 1 - [ ]) - , (constr 1 - [ cse - , (constr 1 - [ (unsafeRatio - 51 - 100) - , (constr 0 - [ ]) ]) ]) ])) - (cse - 2)) ]) + (cse + 2)) + (constr 1 + [ (constr 0 + [ (constr 0 + [ ]) + , (constr 1 + [ cse + , (constr 1 + [ (unsafeRatio + 9 + 10) + , (constr 0 + [ ]) ]) ]) ]) + , (constr 0 + [ ]) ])) ]) (constr 0 [ (constr 1 [ ]) diff --git a/plutus-benchmark/bitwise/test/9.6/Ed25519.golden.uplc b/plutus-benchmark/bitwise/test/9.6/Ed25519.golden.uplc index 3a4a8e84554..78281e16f75 100644 --- a/plutus-benchmark/bitwise/test/9.6/Ed25519.golden.uplc +++ b/plutus-benchmark/bitwise/test/9.6/Ed25519.golden.uplc @@ -966,12 +966,12 @@ (case ds [ (\w - cont -> + rest -> w) ])) (case ds [ (\w - rest -> + cont -> w) ])) ]) (next ipv)) ]) From 74572000349f884152dbae463e7328f40404af77 Mon Sep 17 00:00:00 2001 From: Yuriy Lazaryev Date: Thu, 16 Jul 2026 12:52:33 +0200 Subject: [PATCH 5/6] Use a list literal for the multiIndexArray test indices Builds the index list as a lifted list constant via toBuiltin instead of an mkCons chain, matching the liftCodeDef idiom used for the other array-test inputs. Issue: IntersectMBO/plutus-private#2265 --- .../test/Array/9.12/compiledMultiIndexArray.golden.pir | 8 +------- .../test/Array/9.12/compiledMultiIndexArray.golden.uplc | 4 +--- .../test/Array/9.6/compiledMultiIndexArray.golden.pir | 8 +------- .../test/Array/9.6/compiledMultiIndexArray.golden.uplc | 4 +--- plutus-tx-plugin/test/Array/Spec.hs | 8 ++------ 5 files changed, 6 insertions(+), 26 deletions(-) diff --git a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir index fd5fec816a3..2535175de8d 100644 --- a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir +++ b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.pir @@ -3,13 +3,7 @@ = multiIndexArray in multiIndexArray {data}) - (let - !mkCons : all a. a -> list a -> list a = mkCons - in - mkCons - {integer} - 2 - (mkCons {integer} 0 (mkCons {integer} 0 (mkCons {integer} 1 [])))) + [2,0,0,1] (let !unitval : unit = () in diff --git a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc index 2741d204a36..d46c34f4382 100644 --- a/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc +++ b/plutus-tx-plugin/test/Array/9.12/compiledMultiIndexArray.golden.uplc @@ -2,9 +2,7 @@ 1.1.0 ((\multiIndexArray -> force multiIndexArray) multiIndexArray - ((\mkCons -> - force mkCons 2 (force mkCons 0 (force mkCons 0 (force mkCons 1 [])))) - mkCons) + [2,0,0,1] ((\unitval -> (\mkNilData -> (\mkI -> diff --git a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir index fd5fec816a3..2535175de8d 100644 --- a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir +++ b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.pir @@ -3,13 +3,7 @@ = multiIndexArray in multiIndexArray {data}) - (let - !mkCons : all a. a -> list a -> list a = mkCons - in - mkCons - {integer} - 2 - (mkCons {integer} 0 (mkCons {integer} 0 (mkCons {integer} 1 [])))) + [2,0,0,1] (let !unitval : unit = () in diff --git a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc index 2741d204a36..d46c34f4382 100644 --- a/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc +++ b/plutus-tx-plugin/test/Array/9.6/compiledMultiIndexArray.golden.uplc @@ -2,9 +2,7 @@ 1.1.0 ((\multiIndexArray -> force multiIndexArray) multiIndexArray - ((\mkCons -> - force mkCons 2 (force mkCons 0 (force mkCons 0 (force mkCons 1 [])))) - mkCons) + [2,0,0,1] ((\unitval -> (\mkNilData -> (\mkI -> diff --git a/plutus-tx-plugin/test/Array/Spec.hs b/plutus-tx-plugin/test/Array/Spec.hs index 0e55c5d50fd..25b71b8d579 100644 --- a/plutus-tx-plugin/test/Array/Spec.hs +++ b/plutus-tx-plugin/test/Array/Spec.hs @@ -15,7 +15,7 @@ module Array.Spec where import PlutusCore.Test (goldenUEval) import PlutusTx -import PlutusTx.Builtins (mkNil) +import PlutusTx.Builtins (toBuiltin) import PlutusTx.Builtins.Internal import PlutusTx.Test (goldenPirReadable, goldenUPlcReadable) import Test.Tasty.Extras @@ -71,9 +71,5 @@ compiledIndexArray = compiledMultiIndexArray :: CompiledCode (BuiltinList BuiltinData) compiledMultiIndexArray = $$(compile [||multiIndexArray||]) - `unsafeApplyCode` compiledIndices + `unsafeApplyCode` liftCodeDef (toBuiltin ([2, 0, 0, 1] :: [Integer])) `unsafeApplyCode` compiledListToArray - -compiledIndices :: CompiledCode (BuiltinList BuiltinInteger) -compiledIndices = - $$(compile [||mkCons 2 (mkCons 0 (mkCons 0 (mkCons 1 mkNil)))||]) From 4bca134d06fe9da45b7d13902fc059cec33578a6 Mon Sep 17 00:00:00 2001 From: Yuriy Lazaryev Date: Thu, 16 Jul 2026 12:53:29 +0200 Subject: [PATCH 6/6] Drop the inline comment in the multiIndexArray wrapper Issue: IntersectMBO/plutus-private#2265 --- plutus-tx/src/PlutusTx/Builtins/Internal.hs | 3 --- 1 file changed, 3 deletions(-) diff --git a/plutus-tx/src/PlutusTx/Builtins/Internal.hs b/plutus-tx/src/PlutusTx/Builtins/Internal.hs index 16c086444b4..1518d383a8e 100644 --- a/plutus-tx/src/PlutusTx/Builtins/Internal.hs +++ b/plutus-tx/src/PlutusTx/Builtins/Internal.hs @@ -692,9 +692,6 @@ indexArray (BuiltinArray v) i Fails if any index is not in the range @[0..j)@, where @j@ is the length of the array. -} multiIndexArray :: BuiltinList BuiltinInteger -> BuiltinArray a -> BuiltinList a multiIndexArray (BuiltinList is) (BuiltinArray v) = - -- All indices are checked eagerly in the 'Integer' domain, matching the builtin: an index - -- exceeding @maxBound :: Int@ is out-of-bounds rather than wrapping on conversion, and any - -- out-of-bounds index fails the whole call even if the result list is never fully demanded. case traverse lookupIndex is of Just els -> BuiltinList els Nothing -> Haskell.error "array index out of bounds"