Skip to content

Commit 71acccb

Browse files
authored
SCP-2758: Encode out-of-range integers as bigints (IntersectMBO#3936)
Previously `fromIntegral :: Integer -> Word64` would silently wrap integers which were out of range. Instead we now encode out-of-range integers as big integers, which is more honest *and* will fail to deserialise, making this a "soft" way of failing, rather than calling error.
1 parent 4e04843 commit 71acccb

File tree

2 files changed

+11
-4
lines changed

2 files changed

+11
-4
lines changed

plutus-core/plutus-core/src/PlutusCore/Data.hs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{-# LANGUAGE LambdaCase #-}
55
{-# LANGUAGE MultiWayIf #-}
66
{-# LANGUAGE OverloadedStrings #-}
7+
{-# LANGUAGE TypeApplications #-}
78
{-# LANGUAGE ViewPatterns #-}
89

910
module PlutusCore.Data (Data (..)) where
@@ -112,7 +113,13 @@ encodeData = \case
112113
-- See Note [CBOR alternative tags]
113114
Constr i ds | 0 <= i && i < 7 -> CBOR.encodeTag (fromIntegral (121 + i)) <> encode ds
114115
Constr i ds | 7 <= i && i < 128 -> CBOR.encodeTag (fromIntegral (1280 + (i - 7))) <> encode ds
115-
Constr i ds | otherwise -> CBOR.encodeTag 102 <> CBOR.encodeListLen 2 <> CBOR.encodeWord64 (fromIntegral i) <> encode ds
116+
Constr i ds | otherwise ->
117+
let tagEncoding = if fromIntegral (minBound @Word64) <= i && i <= fromIntegral (maxBound @Word64)
118+
then CBOR.encodeWord64 (fromIntegral i)
119+
-- This is a "correct"-ish encoding of the tag, but it will *not* deserialise, since we insist on a
120+
-- 'Word64' when we deserialise. So this is really a "soft" failure, without using 'error' or something.
121+
else CBOR.encodeInteger i
122+
in CBOR.encodeTag 102 <> CBOR.encodeListLen 2 <> tagEncoding <> encode ds
116123
Map es -> CBOR.encodeMapLen (fromIntegral $ length es) <> mconcat [ encode t <> encode t' | (t, t') <-es ]
117124
List ds -> encode ds
118125
I i -> encodeInteger i
@@ -255,7 +262,6 @@ decodeConstr = CBOR.decodeTag64 >>= \case
255262
decodeConstrExtended = do
256263
len <- CBOR.decodeListLenOrIndef
257264
i <- CBOR.decodeWord64
258-
unless (i >= 0) $ fail ("Invalid negative constructor tag: " ++ show i)
259265
args <- decodeListOf decodeData
260266
case len of
261267
Nothing -> do

plutus-tx/test/Spec.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import qualified Codec.Serialise as Serialise
99
import Control.Exception (ErrorCall, catch)
1010
import qualified Data.ByteString as BS
1111
import Data.Either (isLeft)
12+
import Data.Word
1213
import Hedgehog (MonadGen, Property, PropertyT, annotateShow, assert, forAll, property, tripping)
1314
import qualified Hedgehog.Gen as Gen
1415
import qualified Hedgehog.Range as Range
@@ -123,7 +124,7 @@ sixtyFourByteInteger = 2^((64 :: Integer) *8)
123124
genData :: MonadGen m => m Data
124125
genData =
125126
let st = Gen.subterm genData id
126-
positiveInteger = Gen.integral (Range.linear 0 100000)
127+
constrIndex = fromIntegral <$> (Gen.integral @_ @Word64 Range.linearBounded)
127128
reasonableInteger = Gen.integral (Range.linear (-100000) 100000)
128129
-- over 64 bytes
129130
reallyBigInteger = Gen.integral (Range.linear sixtyFourByteInteger (sixtyFourByteInteger * 2))
@@ -138,7 +139,7 @@ genData =
138139
, I <$> reallyBigInteger
139140
, I <$> reallyBigNInteger
140141
, B <$> someBytes ]
141-
[ Constr <$> positiveInteger <*> constructorArgList
142+
[ Constr <$> constrIndex <*> constructorArgList
142143
, List <$> constructorArgList
143144
, Map <$> kvMapList
144145
]

0 commit comments

Comments
 (0)