| {-# LANGUAGE DataKinds, GADTs, KindSignatures, TypeOperators, ScopedTypeVariables #-} |
| {-# LANGUAGE StrictData, BangPatterns, PatternSynonyms, ViewPatterns #-} |
| {-# LANGUAGE OverloadedStrings, RecordWildCards, DeriveGeneric, RankNTypes #-} |
| {-# LANGUAGE TypeFamilies, PolyKinds, ConstraintKinds, QuantifiedConstraints #-} |
| {-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, FlexibleInstances #-} |
| {-# LANGUAGE TemplateHaskell #-} |
|
|
| |
| |
| |
| |
|
|
| module LiquidLean.QuantumPiper |
| ( QPManifest(..) |
| , QPImage(..) |
| , QPRuntime(..) |
| , ExecutionResult(..) |
| , PipelineStage(..) |
| , executeManifest |
| , buildImage |
| , initRuntime |
| ) where |
|
|
| import Data.Text (Text) |
| import qualified Data.Text as T |
| import Data.ByteString (ByteString) |
| import qualified Data.ByteString as BS |
| import Data.Aeson (ToJSON, FromJSON, encode, decode, eitherDecode, object, (.=), withObject, (.:), Parser) |
| import GHC.Generics (Generic) |
| import Control.Monad.State.Strict |
| import Control.Monad.Except |
| import Control.Concurrent.STM |
| import Control.Concurrent.Async |
| import Data.List (foldl', sortBy) |
| import Data.Map.Strict (Map) |
| import qualified Data.Map.Strict as Map |
| import Data.Set (Set) |
| import qualified Data.Set as Set |
| import Data.Word (Word64, Word32, Word16, Word8) |
| import Data.Int (Int64) |
| import Data.Bits (xor, shiftR, shiftL, (.&.)) |
| import Crypto.Hash (SHA3_256, hash, Digest) |
| import Crypto.PubKey.Ed25519 (PublicKey, SecretKey, sign, verify, toPublic, secretKey) |
| import qualified Data.ByteArray as BA |
| import Data.Time.Clock.POSIX (getPOSIXTime) |
| import Data.Maybe (fromMaybe, catMaybes, mapMaybe) |
| import System.FilePath ((</>), takeDirectory, takeFileName) |
| import System.Directory (createDirectoryIfMissing, doesFileExist, removePathForcibly) |
| import Foreign.C.Types |
| import Foreign.C.String |
| import Foreign.Ptr |
| import Foreign.Storable |
| import System.Process (callProcess) |
|
|
| |
| |
| |
|
|
| type TeamID = Text |
| type ArtifactHash = ByteString |
| type TxHash = ByteString |
| type ArtifactStore = TVar (Map ArtifactHash QArtifact) |
| type CapabilityStore = TVar (Map (Realm, TeamID) Capability) |
|
|
| data Realm |
| = Hamiltonian | Trotter | Pulse | Verification | QuantumIR | Runtime | WORM |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON Realm |
| instance FromJSON Realm |
| |
| data Permission = Read | Write | Verify | Calibrate | Deploy | Attest | Admin |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON Permission |
| instance FromJSON Permission |
| |
| data Capability = Capability |
| { capRealm :: Realm |
| , capTeam :: TeamID |
| , capPermissions :: Set Permission |
| , capDenyPermissions :: Set Permission |
| , capExpiration :: Maybe Word64 |
| , capNotes :: Text |
| } deriving (Show, Generic) |
| |
| instance ToJSON Capability |
| instance FromJSON Capability |
| |
| data ArtifactType |
| = FortranModule | CmmModule | MLIRModule | LLVMModule | PulseSchedule |
| | IsabelleTheorem | ProofCertificate | ConfigFile |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON ArtifactType |
| instance FromJSON ArtifactType |
| |
| data VerificationStatus |
| = Unverified | TypeChecked | Alive2Verified [Text] | IsabelleProven | QuantumValidated |
| deriving (Show, Generic) |
| |
| instance ToJSON VerificationStatus |
| instance FromJSON VerificationStatus |
| |
| data QuantumProperties = QuantumProperties |
| { qpQubits :: Maybe Int |
| , qpGateDepth :: Maybe Int |
| , qpEntanglement :: Maybe Double |
| } deriving (Show, Generic) |
| |
| instance ToJSON QuantumProperties |
| instance FromJSON QuantumProperties |
| |
| defaultQuantumProps :: QuantumProperties |
| defaultQuantumProps = QuantumProperties Nothing Nothing Nothing |
| |
| data ArtifactMetadata = ArtifactMetadata |
| { amCreated :: Word64 |
| , amTeam :: TeamID |
| , amStatus :: VerificationStatus |
| , amQuantumProps :: QuantumProperties |
| , amSize :: Word64 |
| , amImmutable :: Bool |
| } deriving (Show, Generic) |
| |
| instance ToJSON ArtifactMetadata |
| instance FromJSON ArtifactMetadata |
| |
| data QArtifact = QArtifact |
| { qaHash :: ArtifactHash |
| , qaType :: ArtifactType |
| , qaRealm :: Realm |
| , qaTeam :: TeamID |
| , qaContent :: ByteString |
| , qaMetadata :: ArtifactMetadata |
| , qaDeps :: Set ArtifactHash |
| , qaWORMAnchor :: Maybe TxHash |
| } deriving (Show, Generic) |
| |
| instance ToJSON QArtifact |
| instance FromJSON QArtifact |
| |
| data QWorkspace = QWorkspace |
| { wsRoot :: FilePath |
| , wsTeam :: TeamID |
| , wsArtifacts :: ArtifactStore |
| , wsCapabilities :: CapabilityStore |
| , wsWORM :: WORMChain |
| } deriving (Generic) |
| |
| data WORMChain = WORMChain |
| { wcHead :: TVar BlockHeader |
| , wcStore :: TVar (Map TxHash Block) |
| , wcEd25519Key :: SecretKey |
| } deriving (Generic) |
| |
| data BlockHeader = BlockHeader |
| { bhHeight :: Word64 |
| , bhMerkle :: Text |
| , bhHash :: TxHash |
| , bhTimestamp :: Word64 |
| } deriving (Show, Generic) |
| |
| data Block = Block |
| { bHeader :: BlockHeader |
| , bTransactions :: [WORMTx] |
| } deriving (Show, Generic) |
| |
| data TxType = Theorem3Proof | QuantumExecution | PulseCompilation |
| deriving (Show, Eq, Generic) |
| |
| data WORMTx = WORMTx |
| { wtType :: TxType |
| , wtData :: ByteString |
| , wtSignature :: ByteString |
| } deriving (Show, Generic) |
| |
| |
| |
| |
| |
| data MLIRPass = Canonicalize | CSE | QuantumGateFusion | LoopFusion | Vectorize |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON MLIRPass |
| instance FromJSON MLIRPass |
| |
| data OptLevel = O0 | O1 | O2 | O3 | Os | Oz |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON OptLevel |
| instance FromJSON OptLevel |
| |
| data TargetArch = X86_64 | ARM64 | PTX | WASM32 |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON TargetArch |
| instance FromJSON TargetArch |
| |
| data OverlayFS = OverLayFS |
| { ofLayers :: Map FilePath ArtifactHash |
| , ofMounts :: Map FilePath (Set FilePath) |
| , ofDirty :: Set FilePath |
| } deriving (Show, Generic) |
| |
| data ProofCache = ProofCache |
| { pcStore :: TVar (Map Text ByteString) |
| , pcPeers :: TVar [Text] |
| , pcPolicy :: CachePolicy |
| , pcMaxSize :: Word64 |
| , pcTTL :: Word64 |
| , pcMinVerified :: VerificationStatus |
| , pcReplicationFactor :: Int |
| } deriving (Generic) |
| |
| data CachePolicy = CachePolicy |
| deriving (Show, Generic) |
| |
| instance ToJSON CachePolicy |
| instance FromJSON CachePolicy |
| |
| |
| |
| |
| |
| data QPManifest = QPManifest |
| { qpmVersion :: Text |
| , qpmWorkspace :: Text |
| , qpmRealm :: Realm |
| , qpmTeam :: TeamID |
| , qpmInputs :: [ManifestInput] |
| , qpmPipeline :: [PipelineStage] |
| , qpmOutputs :: [ManifestOutput] |
| , qpmEnvironment :: EnvironmentSpec |
| , qpmResources :: ResourceLimits |
| , qpmCapabilities :: [CapabilityReq] |
| , qpmAttestations :: [AttestationReq] |
| } deriving (Show, Generic) |
| |
| instance ToJSON QPManifest |
| instance FromJSON QPManifest |
| |
| data ManifestInput |
| = InputCAS ArtifactHash |
| | InputPath FilePath |
| | InputManifest Text |
| deriving (Show, Generic) |
| |
| instance ToJSON ManifestInput |
| instance FromJSON ManifestInput |
| |
| data ManifestOutput |
| = OutputCAS ArtifactType Text |
| | OutputFile FilePath ArtifactType |
| | OutputWASM FilePath |
| | OutputNative FilePath |
| | OutputPulse FilePath |
| | OutputProof FilePath |
| deriving (Show, Generic) |
| |
| instance ToJSON ManifestOutput |
| instance FromJSON ManifestOutput |
| |
| data PipelineStage |
| = StageFortran FortranConfig |
| | StageCmm CmmConfig |
| | StageMLIR MLIRConfig |
| | StageLLVM LLVMConfig |
| | StageAlive2 Alive2Config |
| | StageIsabelle IsabelleConfig |
| | StageQuantumVerify QuantumVerifyConfig |
| | StagePulseCompile PulseCompileConfig |
| | StageWASM WASMConfig |
| | StageNative NativeConfig |
| | StageCustom CustomStageConfig |
| deriving (Show, Generic) |
| |
| instance ToJSON PipelineStage where |
| toJSON (StageFortran c) = object ["type" .= ("fortran" :: Text), "config" .= c] |
| toJSON (StageCmm c) = object ["type" .= ("cmm" :: Text), "config" .= c] |
| toJSON (StageMLIR c) = object ["type" .= ("mlir" :: Text), "config" .= c] |
| toJSON (StageLLVM c) = object ["type" .= ("llvm" :: Text), "config" .= c] |
| toJSON (StageAlive2 c) = object ["type" .= ("alive2" :: Text), "config" .= c] |
| toJSON (StageIsabelle c) = object ["type" .= ("isabelle" :: Text), "config" .= c] |
| toJSON (StageQuantumVerify c) = object ["type" .= ("quantum-verify" :: Text), "config" .= c] |
| toJSON (StagePulseCompile c) = object ["type" .= ("pulse-compile" :: Text), "config" .= c] |
| toJSON (StageWASM c) = object ["type" .= ("wasm" :: Text), "config" .= c] |
| toJSON (StageNative c) = object ["type" .= ("native" :: Text), "config" .= c] |
| toJSON (StageCustom c) = object ["type" .= ("custom" :: Text), "config" .= c] |
| |
| instance FromJSON PipelineStage where |
| parseJSON = withObject "PipelineStage" $ \v -> do |
| stageType <- v .: "type" :: Parser Text |
| config <- v .: "config" |
| case stageType of |
| "fortran" -> StageFortran <$> parseJSON config |
| "cmm" -> StageCmm <$> parseJSON config |
| "mlir" -> StageMLIR <$> parseJSON config |
| "llvm" -> StageLLVM <$> parseJSON config |
| "alive2" -> StageAlive2 <$> parseJSON config |
| "isabelle" -> StageIsabelle <$> parseJSON config |
| "quantum-verify" -> StageQuantumVerify <$> parseJSON config |
| "pulse-compile" -> StagePulseCompile <$> parseJSON config |
| "wasm" -> StageWASM <$> parseJSON config |
| "native" -> StageNative <$> parseJSON config |
| "custom" -> StageCustom <$> parseJSON config |
| _ -> fail $ "Unknown PipelineStage type: " ++ T.unpack stageType |
|
|
| data FortranConfig = FortranConfig |
| { fcSourceFiles :: [FilePath] |
| , fcFlags :: [Text] |
| , fcOutputModule :: Text |
| , fcOptimization :: OptLevel |
| } deriving (Show, Generic) |
| |
| instance ToJSON FortranConfig |
| instance FromJSON FortranConfig |
| |
| data CmmConfig = CmmConfig |
| { ccInputModule :: Text |
| , ccOutputModule :: Text |
| , ccTarget :: TargetArch |
| } deriving (Show, Generic) |
| |
| instance ToJSON CmmConfig |
| instance FromJSON CmmConfig |
| |
| data MLIRConfig = MLIRConfig |
| { mcInputModule :: Text |
| , mcOutputModule :: Text |
| , mcPasses :: [MLIRPass] |
| , mcDialects :: [Text] |
| } deriving (Show, Generic) |
| |
| instance ToJSON MLIRConfig |
| instance FromJSON MLIRConfig |
| |
| data LLVMConfig = LLVMConfig |
| { lcInputModule :: Text |
| , lcOutputModule :: Text |
| , lcTargetTriple :: Text |
| , lcOptLevel :: OptLevel |
| , lcEnableVerifier :: Bool |
| } deriving (Show, Generic) |
| |
| instance ToJSON LLVMConfig |
| instance FromJSON LLVMConfig |
| |
| data Alive2Config = Alive2Config |
| { acSpecFile :: FilePath |
| , acSourceIR :: Text |
| , acTargetIR :: Text |
| , acTimeout :: Int |
| } deriving (Show, Generic) |
| |
| instance ToJSON Alive2Config |
| instance FromJSON Alive2Config |
| |
| data IsabelleConfig = IsabelleConfig |
| { icTheoryFile :: FilePath |
| , icImports :: [Text] |
| , icProofMethod :: Text |
| } deriving (Show, Generic) |
| |
| instance ToJSON IsabelleConfig |
| instance FromJSON IsabelleConfig |
| |
| data QuantumVerifyConfig = QuantumVerifyConfig |
| { qvcCircuitFile :: FilePath |
| , qvcChecks :: [QuantumCheck] |
| } deriving (Show, Generic) |
| |
| instance ToJSON QuantumVerifyConfig |
| instance FromJSON QuantumVerifyConfig |
| |
| data QuantumCheck |
| = CheckUnitarity | CheckNoCloning | CheckLinearity | CheckQubitCount Int |
| | CheckDepth Int | CheckEntanglement Text |
| deriving (Show, Generic) |
| |
| instance ToJSON QuantumCheck |
| instance FromJSON QuantumCheck |
| |
| data PulseCompileConfig = PulseCompileConfig |
| { pccBackend :: Text |
| , pccTarget :: PulseTarget |
| , pccConstraints :: PulseConstraints |
| } deriving (Show, Generic) |
| |
| instance ToJSON PulseCompileConfig |
| instance FromJSON PulseCompileConfig |
| |
| data PulseTarget = PulseIBM | PulseOpenPulse | PulseCustom Text |
| deriving (Show, Generic) |
| |
| instance ToJSON PulseTarget |
| instance FromJSON PulseTarget |
| |
| data PulseConstraints = PulseConstraints |
| { pcMaxAmplitude :: Double |
| , pcMaxFrequency :: Double |
| , pcSampleRate :: Word64 |
| , pcAlignment :: Word64 |
| , pcCrossTalkSuppression :: Bool |
| } deriving (Show, Generic) |
| |
| instance ToJSON PulseConstraints |
| instance FromJSON PulseConstraints |
| |
| data WASMConfig = WASMConfig |
| { wcInputModule :: Text |
| , wcOutputFile :: FilePath |
| , wcImports :: [Text] |
| , wcExports :: [Text] |
| } deriving (Show, Generic) |
| |
| instance ToJSON WASMConfig |
| instance FromJSON WASMConfig |
| |
| data NativeConfig = NativeConfig |
| { ncInputModule :: Text |
| , ncOutputFile :: FilePath |
| , ncTargetTriple :: Text |
| , ncLinkLibs :: [Text] |
| } deriving (Show, Generic) |
| |
| instance ToJSON NativeConfig |
| instance FromJSON NativeConfig |
| |
| data CustomStageConfig = CustomStageConfig |
| { cscCommand :: Text |
| , cscArgs :: [Text] |
| , cscEnv :: Map Text Text |
| , cscInputs :: [Text] |
| , cscOutputs :: [Text] |
| } deriving (Show, Generic) |
| |
| instance ToJSON CustomStageConfig |
| instance FromJSON CustomStageConfig |
| |
| data EnvironmentSpec = EnvironmentSpec |
| { esVariables :: Map Text Text |
| , esMounts :: [MountSpec] |
| , esDevices :: [DeviceSpec] |
| , esNetwork :: NetworkMode |
| , esUser :: Maybe Text |
| } deriving (Show, Generic) |
| |
| instance ToJSON EnvironmentSpec |
| instance FromJSON EnvironmentSpec |
| |
| data MountSpec = MountSpec |
| { msSource :: Text |
| , msTarget :: FilePath |
| , msReadOnly :: Bool |
| , msType :: MountType |
| } deriving (Show, Generic) |
| |
| instance ToJSON MountSpec |
| instance FromJSON MountSpec |
| |
| data MountType = Bind | CAS | Tmpfs | Volume |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON MountType |
| instance FromJSON MountType |
| |
| data DeviceSpec = DeviceSpec |
| { dsPath :: FilePath |
| , dsPermissions :: Text |
| } deriving (Show, Generic) |
| |
| instance ToJSON DeviceSpec |
| instance FromJSON DeviceSpec |
| |
| data NetworkMode = NetworkNone | NetworkHost | NetworkBridge Text |
| deriving (Show, Eq, Ord, Generic) |
| |
| instance ToJSON NetworkMode |
| instance FromJSON NetworkMode |
| |
| data ResourceLimits = ResourceLimits |
| { rlCPU :: Maybe Double |
| , rlMemory :: Maybe Word64 |
| , rlDisk :: Maybe Word64 |
| , rlGPU :: Maybe Int |
| , rlTime :: Maybe Int |
| , rlPIDs :: Maybe Int |
| } deriving (Show, Generic) |
| |
| instance ToJSON ResourceLimits |
| instance FromJSON ResourceLimits |
| |
| defaultResourceLimits :: ResourceLimits |
| defaultResourceLimits = ResourceLimits |
| { rlCPU = Just 4.0 |
| , rlMemory = Just (8 * 1024 * 1024 * 1024) |
| , rlDisk = Just (10 * 1024 * 1024 * 1024) |
| , rlGPU = Just 1 |
| , rlTime = Just 3600 |
| , rlPIDs = Just 256 |
| } |
| |
| data CapabilityReq = CapabilityReq |
| { crRealm :: Realm |
| , crPermissions :: [Permission] |
| } deriving (Show, Generic) |
| |
| instance ToJSON CapabilityReq |
| instance FromJSON CapabilityReq |
| |
| data AttestationReq = AttestationReq |
| { arStage :: Text |
| , arType :: AttestationType |
| } deriving (Show, Generic) |
| |
| instance ToJSON AttestationReq |
| instance FromJSON AttestationReq |
| |
| data AttestationType = AttestBuild | AttestVerify | AttestDeploy | AttestQuantum |
| deriving (Show, Eq, Ord, Generic, Enum, Bounded) |
| |
| instance ToJSON AttestationType |
| instance FromJSON AttestationType |
| |
| |
| |
| |
| |
| data QPImage = QPImage |
| { qpiManifestHash :: ArtifactHash |
| , qpiManifest :: QPManifest |
| , qpiArtifacts :: Map ArtifactHash QArtifact |
| , qpiLayerHashes :: [ArtifactHash] |
| , qpiSignature :: ByteString |
| } deriving (Show, Generic) |
| |
| instance ToJSON QPImage |
| instance FromJSON QPImage |
| |
| data StageResult = StageResult |
| { srStage :: Text |
| , srInputs :: Map Text ArtifactHash |
| , srOutputs :: Map Text ArtifactHash |
| , srLogs :: [Text] |
| , srDuration :: Double |
| , srSuccess :: Bool |
| , srAttestation :: Maybe TxHash |
| } deriving (Show, Generic) |
| |
| instance ToJSON StageResult |
| instance FromJSON StageResult |
| |
| data ExecutionResult = ExecutionResult |
| { erImageHash :: ArtifactHash |
| , erStageResults :: [StageResult] |
| , erOutputs :: [ArtifactHash] |
| , erDuration :: Double |
| , erSuccess :: Bool |
| } deriving (Show, Generic) |
| |
| instance ToJSON ExecutionResult |
| instance FromJSON ExecutionResult |
| |
| data QPRuntime = QPRuntime |
| { qprWorkspace :: QWorkspace |
| , qprImageStore :: TVar (Map ArtifactHash QPImage) |
| } deriving (Generic) |
| |
| |
| |
| |
| |
| foreign import ccall unsafe "bob_theorem3_enforce_genus_zero" |
| c_theorem3_enforce :: CString -> CInt -> IO CInt |
| |
| foreign import ccall unsafe "bob_worm_chain_checkpoint" |
| c_worm_checkpoint :: Ptr () -> CString -> IO CInt |
| |
| foreign import ccall unsafe "bob_worm_chain_restore" |
| c_worm_restore :: Ptr () -> CString -> IO CInt |
| |
| |
| |
| |
| |
| buildImage :: QWorkspace -> QPManifest -> IO (Either String QPImage) |
| buildImage ws manifest = do |
| let manifestBytes = encode manifest |
| let manifestHash = hash manifestBytes |
| let image = QPImage |
| { qpiManifestHash = manifestHash |
| , qpiManifest = manifest |
| , qpiArtifacts = Map.empty |
| , qpiLayerHashes = [] |
| , qpiSignature = "" |
| } |
| pure (Right image) |
| |
| executePipeline :: QWorkspace -> QPManifest -> IO (Either String (Map ArtifactHash QArtifact, [StageResult])) |
| executePipeline ws manifest = do |
| pure (Right (Map.empty, [])) |
| |
| executeManifest :: QPRuntime -> QPManifest -> IO (Either String ExecutionResult) |
| executeManifest runtime manifest = do |
| startTime <- getPOSIXTime |
| let ws = qprWorkspace runtime |
| |
| result <- executePipeline ws manifest |
| case result of |
| Left err -> pure (Left err) |
| Right (finalArtifacts, stageResults) -> do |
| imageResult <- buildImage ws manifest |
| case imageResult of |
| Left err -> pure (Left err) |
| Right image -> do |
| atomically $ modifyTVar' (qprImageStore runtime) (Map.insert (qpiManifestHash image) image) |
| endTime <- getPOSIXTime |
| pure (Right ExecutionResult |
| { erImageHash = qpiManifestHash image |
| , erStageResults = stageResults |
| , erOutputs = Map.keys finalArtifacts |
| , erDuration = realToFrac (endTime - startTime) |
| , erSuccess = True |
| }) |
| |
| initRuntime :: FilePath -> TeamID -> IO QPRuntime |
| initRuntime root team = do |
| |
| |
| wcHeadVar <- newTVarIO (BlockHeader 0 "" "" 0) |
| wcStoreVar <- newTVarIO Map.empty |
| |
| |
| let teamStr = T.unpack team |
| teamHash = hash (BS.pack teamStr) :: Digest SHA3_256 |
| seedBytes = BA.take 32 (BA.convert teamHash) :: ByteString |
| |
| case secretKey seedBytes of |
| Just sk -> do |
| let worm = WORMChain wcHeadVar wcStoreVar sk |
| |
| |
| artifactsVar <- newTVarIO Map.empty |
| capsVar <- newTVarIO Map.empty |
| |
| let ws = QWorkspace root team artifactsVar capsVar worm |
| |
| |
| imageStoreVar <- newTVarIO Map.empty |
| |
| pure QPRuntime |
| { qprWorkspace = ws |
| , qprImageStore = imageStoreVar |
| } |
| Nothing -> fail "initRuntime: Failed to derive Ed25519 key from team ID" |
| |