Skip to content

Commit

Permalink
Support random API from the Anoma stdlib (#3129)
Browse files Browse the repository at this point in the history
This PR adds frontend support for the Anoma Random API:

The frontend builtin APIs are:

```
builtin anoma-random-generator
axiom RandomGenerator : Type;

builtin anoma-random-generator-init
axiom randomGeneratorInit : Nat -> RandomGenerator;

builtin anoma-random-generator-split
axiom randomGeneratorSplit : RandomGenerator
  -> Pair RandomGenerator RandomGenerator;

builtin anoma-random-next-bytes
axiom randomNextBytes : Nat
  -> RandomGenerator
  -> Pair ByteArray RandomGenerator;
```

### Nockma Evaluator

The Nockma evaluator intercepts the corresponding Anoma random stdlib
calls using the
[System.Random](https://hackage.haskell.org/package/random-1.2.1.2/docs/System-Random.html)
API. The implementation uses the
[splitmix](https://hackage.haskell.org/package/splitmix-0.1.0.5/docs/System-Random-SplitMix.html)
generator directly because it has an API to destructure the generator
into a pair of integers. We can use this to serialise the generator.



* Closes #2902
  • Loading branch information
paulcadman authored Oct 29, 2024
1 parent c143259 commit 3b34f6e
Show file tree
Hide file tree
Showing 33 changed files with 362 additions and 1 deletion.
2 changes: 2 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,13 @@ dependencies:
- prettyprinter-ansi-terminal == 1.1.*
- primitive == 0.9.*
- process == 1.6.*
- random == 1.2.*
- safe == 0.3.*
- scientific == 0.3.*
- singletons == 3.0.*
- singletons-base == 3.3.*
- singletons-th == 3.3.*
- splitmix == 0.1.*
- stm == 2.5.*
- Stream == 0.4.*
- string-interpolate == 0.3.*
Expand Down
31 changes: 31 additions & 0 deletions src/Juvix/Compiler/Builtins/Anoma.hs
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,34 @@ checkZeroDelta f = do
delta <- getBuiltinNameScoper l BuiltinAnomaDelta
unless (f ^. axiomType === delta) $
builtinsErrorText (getLoc f) "zeroDelta must be of Delta"

checkAnomaRandomGenerator :: (Members '[Error ScoperError] r) => AxiomDef -> Sem r ()
checkAnomaRandomGenerator d =
unless (isSmallUniverse' (d ^. axiomType)) $
builtinsErrorText (getLoc d) "AnomaRandomGenerator should be in the small universe"

checkAnomaRandomGeneratorInit :: (Members '[Reader BuiltinsTable, Error ScoperError] r) => AxiomDef -> Sem r ()
checkAnomaRandomGeneratorInit f = do
let l = getLoc f
gen <- getBuiltinNameScoper l BuiltinAnomaRandomGenerator
nat_ <- getBuiltinNameScoper l BuiltinNat
unless (f ^. axiomType === (nat_ --> gen)) $
builtinsErrorText l "initRandomGenerator must be of type Nat -> AnomaRandomGenerator"

checkAnomaRandomNextBytes :: (Members '[Reader BuiltinsTable, Error ScoperError] r) => AxiomDef -> Sem r ()
checkAnomaRandomNextBytes f = do
let l = getLoc f
gen <- getBuiltinNameScoper l BuiltinAnomaRandomGenerator
nat_ <- getBuiltinNameScoper l BuiltinNat
bytearray <- getBuiltinNameScoper l BuiltinByteArray
pair_ <- getBuiltinNameScoper l BuiltinPair
unless (f ^. axiomType === (nat_ --> gen --> (pair_ @@ bytearray @@ gen))) $
builtinsErrorText l "nextBytes must be of type Nat -> AnomaRandomGenerator -> Pair ByteArray AnomaRandomGenerator"

checkAnomaRandomSplit :: (Members '[Reader BuiltinsTable, Error ScoperError] r) => AxiomDef -> Sem r ()
checkAnomaRandomSplit f = do
let l = getLoc f
gen <- getBuiltinNameScoper l BuiltinAnomaRandomGenerator
pair_ <- getBuiltinNameScoper l BuiltinPair
unless (f ^. axiomType === (gen --> pair_ @@ gen @@ gen)) $
builtinsErrorText l "randomSplit must be of type AnomaRandomGenerator -> Pair AnomaRandomGenerator AnomaRandomGenerator"
12 changes: 12 additions & 0 deletions src/Juvix/Compiler/Concrete/Data/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ data BuiltinAxiom
| BuiltinAnomaSubDelta
| BuiltinAnomaProveAction
| BuiltinAnomaProveDelta
| BuiltinAnomaRandomGenerator
| BuiltinAnomaRandomGeneratorInit
| BuiltinAnomaRandomNextBytes
| BuiltinAnomaRandomSplit
| BuiltinPoseidon
| BuiltinEcOp
| BuiltinRandomEcPoint
Expand Down Expand Up @@ -311,6 +315,10 @@ instance HasNameKind BuiltinAxiom where
BuiltinAnomaSubDelta -> KNameFunction
BuiltinAnomaProveAction -> KNameFunction
BuiltinAnomaProveDelta -> KNameFunction
BuiltinAnomaRandomGenerator -> KNameInductive
BuiltinAnomaRandomGeneratorInit -> KNameFunction
BuiltinAnomaRandomNextBytes -> KNameFunction
BuiltinAnomaRandomSplit -> KNameFunction
BuiltinPoseidon -> KNameFunction
BuiltinEcOp -> KNameFunction
BuiltinRandomEcPoint -> KNameFunction
Expand Down Expand Up @@ -378,6 +386,10 @@ instance Pretty BuiltinAxiom where
BuiltinAnomaSubDelta -> Str.anomaSubDelta
BuiltinAnomaProveDelta -> Str.anomaProveDelta
BuiltinAnomaProveAction -> Str.anomaProveAction
BuiltinAnomaRandomGenerator -> Str.anomaRandomGenerator
BuiltinAnomaRandomGeneratorInit -> Str.anomaRandomGeneratorInit
BuiltinAnomaRandomNextBytes -> Str.anomaRandomNextBytes
BuiltinAnomaRandomSplit -> Str.anomaRandomSplit
BuiltinPoseidon -> Str.cairoPoseidon
BuiltinEcOp -> Str.cairoEcOp
BuiltinRandomEcPoint -> Str.cairoRandomEcPoint
Expand Down
3 changes: 3 additions & 0 deletions src/Juvix/Compiler/Core/Evaluator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ geval opts herr tab env0 = eval' env0
OpAnomaZeroDelta -> normalizeOrUnsupported opcode
OpAnomaAddDelta -> normalizeOrUnsupported opcode
OpAnomaSubDelta -> normalizeOrUnsupported opcode
OpAnomaRandomGeneratorInit -> normalizeOrUnsupported opcode
OpAnomaRandomNextBytes -> normalizeOrUnsupported opcode
OpAnomaRandomSplit -> normalizeOrUnsupported opcode
OpPoseidonHash -> poseidonHashOp
OpEc -> ecOp
OpRandomEcPoint -> randomEcPointOp
Expand Down
6 changes: 6 additions & 0 deletions src/Juvix/Compiler/Core/Extra/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ mkTypeByteArray i = mkTypePrim i PrimByteArray
mkTypeByteArray' :: Type
mkTypeByteArray' = mkTypeByteArray Info.empty

mkTypeRandomGenerator :: Info -> Type
mkTypeRandomGenerator i = mkTypePrim i PrimRandomGenerator

mkTypeRandomGenerator' :: Type
mkTypeRandomGenerator' = mkTypeRandomGenerator Info.empty

mkDynamic :: Info -> Type
mkDynamic i = NDyn (DynamicTy i)

Expand Down
6 changes: 6 additions & 0 deletions src/Juvix/Compiler/Core/Extra/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ isDebugOp = \case
OpAnomaZeroDelta -> False
OpAnomaAddDelta -> False
OpAnomaSubDelta -> False
OpAnomaRandomGeneratorInit -> False
OpAnomaRandomNextBytes -> False
OpAnomaRandomSplit -> False
OpEc -> False
OpFieldAdd -> False
OpFieldDiv -> False
Expand Down Expand Up @@ -504,6 +507,9 @@ builtinOpArgTypes = \case
OpAnomaZeroDelta -> []
OpAnomaAddDelta -> [mkDynamic', mkDynamic']
OpAnomaSubDelta -> [mkDynamic', mkDynamic']
OpAnomaRandomGeneratorInit -> [mkTypeInteger']
OpAnomaRandomNextBytes -> [mkTypeInteger', mkTypeRandomGenerator']
OpAnomaRandomSplit -> [mkTypeRandomGenerator']
OpPoseidonHash -> [mkDynamic']
OpEc -> [mkDynamic', mkTypeField', mkDynamic']
OpRandomEcPoint -> []
Expand Down
3 changes: 3 additions & 0 deletions src/Juvix/Compiler/Core/Keywords.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import Juvix.Data.Keyword.All
kwAnomaEncode,
kwAnomaProveAction,
kwAnomaProveDelta,
kwAnomaRandomGeneratorInit,
kwAnomaRandomNextBytes,
kwAnomaRandomSplit,
kwAnomaResourceCommitment,
kwAnomaResourceDelta,
kwAnomaResourceKind,
Expand Down
14 changes: 13 additions & 1 deletion src/Juvix/Compiler/Core/Language/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ data BuiltinOp
| OpAnomaZeroDelta
| OpAnomaAddDelta
| OpAnomaSubDelta
| OpAnomaRandomGeneratorInit
| OpAnomaRandomNextBytes
| OpAnomaRandomSplit
| OpPoseidonHash
| OpEc
| OpRandomEcPoint
Expand Down Expand Up @@ -136,6 +139,9 @@ builtinOpArgsNum = \case
OpAnomaZeroDelta -> 0
OpAnomaAddDelta -> 2
OpAnomaSubDelta -> 2
OpAnomaRandomGeneratorInit -> 1
OpAnomaRandomNextBytes -> 2
OpAnomaRandomSplit -> 1
OpPoseidonHash -> 1
OpEc -> 3
OpRandomEcPoint -> 0
Expand Down Expand Up @@ -198,6 +204,9 @@ builtinIsFoldable = \case
OpAnomaAddDelta -> False
OpAnomaSubDelta -> False
OpAnomaSha256 -> False
OpAnomaRandomGeneratorInit -> False
OpAnomaRandomNextBytes -> False
OpAnomaRandomSplit -> False
OpPoseidonHash -> False
OpEc -> False
OpRandomEcPoint -> False
Expand Down Expand Up @@ -234,7 +243,10 @@ builtinsAnoma =
OpAnomaProveDelta,
OpAnomaZeroDelta,
OpAnomaAddDelta,
OpAnomaSubDelta
OpAnomaSubDelta,
OpAnomaRandomGeneratorInit,
OpAnomaRandomNextBytes,
OpAnomaRandomSplit
]

builtinsUInt8 :: [BuiltinOp]
Expand Down
1 change: 1 addition & 0 deletions src/Juvix/Compiler/Core/Language/Primitives.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data Primitive
| PrimString
| PrimField
| PrimByteArray
| PrimRandomGenerator
deriving stock (Eq, Generic)

primitiveUInt8 :: Primitive
Expand Down
13 changes: 13 additions & 0 deletions src/Juvix/Compiler/Core/Pretty/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ instance PrettyCode BuiltinOp where
OpAnomaZeroDelta -> return primZeroDelta
OpAnomaAddDelta -> return primAddDelta
OpAnomaSubDelta -> return primSubDelta
OpAnomaRandomGeneratorInit -> return primRandomGeneratorInit
OpAnomaRandomNextBytes -> return primRandomNextBytes
OpAnomaRandomSplit -> return primRandomSplit
OpPoseidonHash -> return primPoseidonHash
OpEc -> return primEc
OpRandomEcPoint -> return primRandomEcPoint
Expand Down Expand Up @@ -105,6 +108,7 @@ instance PrettyCode Primitive where
PrimBool _ -> return $ annotate (AnnKind KNameInductive) (pretty ("Bool" :: String))
PrimString -> return $ annotate (AnnKind KNameInductive) (pretty ("String" :: String))
PrimByteArray -> return $ annotate (AnnKind KNameInductive) (pretty ("ByteArray" :: String))
PrimRandomGenerator -> return $ annotate (AnnKind KNameInductive) (pretty ("RandomGenerator" :: String))

ppName :: NameKind -> Text -> Sem r (Doc Ann)
ppName kind name = return $ annotate (AnnKind kind) (pretty name)
Expand Down Expand Up @@ -954,6 +958,15 @@ primAddDelta = primitive Str.anomaAddDelta
primSubDelta :: Doc Ann
primSubDelta = primitive Str.anomaSubDelta

primRandomGeneratorInit :: Doc Ann
primRandomGeneratorInit = primitive Str.anomaRandomGeneratorInit

primRandomNextBytes :: Doc Ann
primRandomNextBytes = primitive Str.anomaRandomNextBytes

primRandomSplit :: Doc Ann
primRandomSplit = primitive Str.anomaRandomSplit

primPoseidonHash :: Doc Ann
primPoseidonHash = primitive Str.cairoPoseidon

Expand Down
1 change: 1 addition & 0 deletions src/Juvix/Compiler/Core/Transformation/Check/Cairo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ checkCairo md = do
PrimField {} -> True
PrimString {} -> False
PrimByteArray {} -> False
PrimRandomGenerator {} -> False

isRecordOrList :: TypeConstr -> Bool
isRecordOrList TypeConstr {..} = case ii ^. inductiveBuiltin of
Expand Down
3 changes: 3 additions & 0 deletions src/Juvix/Compiler/Core/Transformation/ComputeTypeInfo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ computeNodeTypeInfo md = umapL go
OpAnomaZeroDelta -> mkDynamic'
OpAnomaAddDelta -> mkDynamic'
OpAnomaSubDelta -> mkDynamic'
OpAnomaRandomGeneratorInit -> mkTypeRandomGenerator'
OpAnomaRandomNextBytes -> mkDynamic'
OpAnomaRandomSplit -> mkDynamic'
OpPoseidonHash -> case _builtinAppArgs of
[arg] -> Info.getNodeType arg
_ -> error "incorrect poseidon builtin application"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ convertNode md = umap go
Just (BuiltinTypeAxiom BuiltinField) -> mkTypeField'
Just (BuiltinTypeAxiom BuiltinByte) -> mkTypeUInt8'
Just (BuiltinTypeAxiom BuiltinByteArray) -> mkTypeByteArray'
Just (BuiltinTypeAxiom BuiltinAnomaRandomGenerator) -> mkTypeRandomGenerator'
_ -> node
where
ii = lookupInductiveInfo md _typeConstrSymbol
Expand Down
32 changes: 32 additions & 0 deletions src/Juvix/Compiler/Core/Translation/FromInternal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,10 @@ goAxiomInductive a = whenJust (a ^. Internal.axiomBuiltin) builtinInductive
Internal.BuiltinAnomaZeroDelta -> return ()
Internal.BuiltinAnomaAddDelta -> return ()
Internal.BuiltinAnomaSubDelta -> return ()
Internal.BuiltinAnomaRandomGenerator -> registerInductiveAxiom (Just BuiltinAnomaRandomGenerator) []
Internal.BuiltinAnomaRandomGeneratorInit -> return ()
Internal.BuiltinAnomaRandomNextBytes -> return ()
Internal.BuiltinAnomaRandomSplit -> return ()
Internal.BuiltinPoseidon -> return ()
Internal.BuiltinEcOp -> return ()
Internal.BuiltinRandomEcPoint -> return ()
Expand Down Expand Up @@ -952,6 +956,30 @@ goAxiomDef a = maybe goAxiomNotBuiltin builtinBody (a ^. Internal.axiomBuiltin)
(mkBuiltinApp' OpAnomaSubDelta [mkVar' 1, mkVar' 0])
)
)
Internal.BuiltinAnomaRandomGenerator -> return ()
Internal.BuiltinAnomaRandomGeneratorInit -> do
natType <- getNatType
registerAxiomDef
( mkLambda'
natType
(mkBuiltinApp' OpAnomaRandomGeneratorInit [mkVar' 0])
)
Internal.BuiltinAnomaRandomNextBytes -> do
natType <- getNatType
registerAxiomDef
( mkLambda'
natType
( mkLambda'
mkDynamic'
(mkBuiltinApp' OpAnomaRandomNextBytes [mkVar' 1, mkVar' 0])
)
)
Internal.BuiltinAnomaRandomSplit -> do
registerAxiomDef
( mkLambda'
mkDynamic'
(mkBuiltinApp' OpAnomaRandomSplit [mkVar' 0])
)
Internal.BuiltinPoseidon -> do
psName <- getPoseidonStateName
psSym <- getPoseidonStateSymbol
Expand Down Expand Up @@ -1407,6 +1435,10 @@ goApplication a = do
Just Internal.BuiltinAnomaSubDelta -> app
Just Internal.BuiltinAnomaProveAction -> app
Just Internal.BuiltinAnomaProveDelta -> app
Just Internal.BuiltinAnomaRandomGenerator -> app
Just Internal.BuiltinAnomaRandomGeneratorInit -> app
Just Internal.BuiltinAnomaRandomNextBytes -> app
Just Internal.BuiltinAnomaRandomSplit -> app
Just Internal.BuiltinPoseidon -> app
Just Internal.BuiltinEcOp -> app
Just Internal.BuiltinRandomEcPoint -> app
Expand Down
3 changes: 3 additions & 0 deletions src/Juvix/Compiler/Core/Translation/FromSource.hs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@ builtinAppExpr varsNum vars = do
<|> (kw kwAnomaZeroDelta $> OpAnomaZeroDelta)
<|> (kw kwAnomaAddDelta $> OpAnomaAddDelta)
<|> (kw kwAnomaSubDelta $> OpAnomaSubDelta)
<|> (kw kwAnomaRandomGeneratorInit $> OpAnomaRandomGeneratorInit)
<|> (kw kwAnomaRandomNextBytes $> OpAnomaRandomNextBytes)
<|> (kw kwAnomaRandomSplit $> OpAnomaRandomSplit)

args <- P.many (atom varsNum vars)
return $ mkBuiltinApp' op args
Expand Down
4 changes: 4 additions & 0 deletions src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ fromCore fsize tab =
BuiltinAnomaZeroDelta -> False
BuiltinAnomaProveAction -> False
BuiltinAnomaProveDelta -> False
BuiltinAnomaRandomGenerator -> False
BuiltinAnomaRandomGeneratorInit -> False
BuiltinAnomaRandomNextBytes -> False
BuiltinAnomaRandomSplit -> False
BuiltinPoseidon -> False
BuiltinEcOp -> False
BuiltinRandomEcPoint -> False
Expand Down
4 changes: 4 additions & 0 deletions src/Juvix/Compiler/Internal/Translation/FromConcrete.hs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ checkBuiltinAxiom d b = localBuiltins $ case b of
BuiltinAnomaSubDelta -> checkDeltaBinaryOp d
BuiltinAnomaProveDelta -> checkProveDelta d
BuiltinAnomaProveAction -> checkProveAction d
BuiltinAnomaRandomGenerator -> checkAnomaRandomGenerator d
BuiltinAnomaRandomGeneratorInit -> checkAnomaRandomGeneratorInit d
BuiltinAnomaRandomNextBytes -> checkAnomaRandomNextBytes d
BuiltinAnomaRandomSplit -> checkAnomaRandomSplit d
BuiltinPoseidon -> checkPoseidon d
BuiltinEcOp -> checkEcOp d
BuiltinRandomEcPoint -> checkRandomEcPoint d
Expand Down
12 changes: 12 additions & 0 deletions src/Juvix/Compiler/Nockma/AnomaLib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ anomaLibPath = \case
1
]
|]
-- Obtained from the urbit dojo using:
--
-- => rm != |= [seed=@] ~(. og seed)
StdlibRandomInitGen -> [nock| [8 [1 0] [1 8 [9 47 0 31] 10 [6 0 14] 0 2] 0 1] |]
-- obtained from the urbit dojo using:
--
-- => rm != |= [rng=* width=@] (raws:`_og`rng width)
StdlibRandomNextBytes -> [nock| [8 [1 0 0] [1 8 [7 [0 12] 9 4 0 1] 9 2 10 [6 0 29] 0 2] 0 1] |]
-- obtained from the urbit dojo using:
--
-- => rm != |= [rng=*] split:`_og`rng
StdlibRandomSplit -> [nock| [8 [1 0] [1 7 [0 6] 9 21 0 1] 0 1] |]
AnomaLibFunction (AnomaRmFunction f) -> case f of
RmCommit -> [nock| [9 94 0 1] |]
RmNullify -> [nock| [9 350 0 1] |]
Expand Down
6 changes: 6 additions & 0 deletions src/Juvix/Compiler/Nockma/AnomaLib/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ data StdlibFunction
| StdlibLengthBytes
| StdlibCurry
| StdlibSha256
| StdlibRandomInitGen
| StdlibRandomNextBytes
| StdlibRandomSplit
deriving stock (Show, Lift, Eq, Bounded, Enum, Generic)

instance Hashable StdlibFunction
Expand Down Expand Up @@ -108,6 +111,9 @@ instance Pretty StdlibFunction where
StdlibLengthBytes -> "length-bytes"
StdlibCurry -> "curry"
StdlibSha256 -> "sha256"
StdlibRandomInitGen -> "random-init"
StdlibRandomNextBytes -> "random-next-bytes"
StdlibRandomSplit -> "random-split"

instance Pretty RmFunction where
pretty = \case
Expand Down
Loading

0 comments on commit 3b34f6e

Please sign in to comment.