MultiChor-1.0.0.0: examples/MPCFake.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module MPCFake where
import CLI
import Choreography
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data (TestArgs, reference)
import Data.Kind (Type)
import Data.Maybe (fromJust)
import GHC.TypeLits (KnownSymbol)
import System.Random
import Test.QuickCheck (Arbitrary, arbitrary, chooseInt, elements, getSize, oneof, resize)
$(mkLoc "trusted3rdParty")
$(mkLoc "p1")
$(mkLoc "p2")
$(mkLoc "p3")
$(mkLoc "p4")
xor :: (Foldable f) => f Bool -> Bool
xor = foldr1 (/=)
data Circuit :: [LocTy] -> Type where
InputWire :: (KnownSymbol p) => Member p ps -> Circuit ps
LitWire :: Bool -> Circuit ps
AndGate :: Circuit ps -> Circuit ps -> Circuit ps
XorGate :: Circuit ps -> Circuit ps -> Circuit ps
instance Show (Circuit ps) where
show (InputWire p) = "InputWire<" ++ toLocTm p ++ ">"
show (LitWire b) = "LitWire " ++ show b
show (AndGate left right) = "(" ++ show left ++ ") AND (" ++ show right ++ ")"
show (XorGate left right) = "(" ++ show left ++ ") XOR (" ++ show right ++ ")"
instance Arbitrary (Circuit '["p1", "p2", "p3", "p4"]) where
arbitrary = do
size <- getSize
if 1 >= size
then oneof $ (LitWire <$> arbitrary) : (return <$> [InputWire p1, InputWire p2, InputWire p3, InputWire p4])
else do
left <- chooseInt (1, size)
a <- resize left arbitrary
b <- resize (1 `max` (size - left)) arbitrary
op <- elements [AndGate, XorGate]
return $ a `op` b
data Args = Args
{ circuit :: Circuit '["p1", "p2", "p3", "p4"],
p1in :: Bool, -- These should be lists, but consuming them would be a chore...
p2in :: Bool,
p3in :: Bool,
p4in :: Bool
}
deriving (Show)
instance Arbitrary Args where
arbitrary = Args <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
instance TestArgs Args (Bool, Bool, Bool, Bool) where
reference Args {circuit, p1in, p2in, p3in, p4in} = (answer, answer, answer, answer)
where
recurse c = case c of
InputWire p -> fromJust $ toLocTm p `lookup` inputs
LitWire b -> b
AndGate left right -> recurse left && recurse right
XorGate left right -> recurse left /= recurse right
inputs = ["p1", "p2", "p3", "p4"] `zip` [p1in, p2in, p3in, p4in]
answer = recurse circuit
secretShare ::
forall p parties owners ps m.
(KnownSymbols parties, KnownSymbol p, MonadIO m) =>
Subset parties ps ->
Member p ps ->
(Member p owners, Located owners Bool) ->
Choreo ps m (Faceted parties '[] Bool)
secretShare parties p (ownership, value) = do
shares <- p `locally` \un -> genShares (un ownership value)
PIndexed fs <- scatter p parties shares
return $ PIndexed $ Facet . othersForget (First @@ nobody) . getFacet . fs
where
genShares x = case tySpine @parties of
TyCons -> gs'
TyNil -> error "Can't secret-share to zero people."
where
gs' :: forall q qs. (KnownSymbol q, KnownSymbols qs) => m (Quire (q ': qs) Bool)
gs' = do
freeShares <- sequence $ pure $ liftIO randomIO -- generate n-1 random shares
return $ xor (qCons @q x freeShares) `qCons` freeShares
reveal ::
forall ps m.
(KnownSymbols ps) =>
Faceted ps '[] Bool ->
Choreo ps m Bool
reveal shares = do
let ps = allOf @ps
allShares <- gather ps ps shares
value <- ps `congruently` \un -> xor $ un ps allShares
naked ps value
computeWire ::
(KnownSymbols ps, KnownSymbols parties, KnownSymbol trustedAnd, MonadIO m) =>
Member trustedAnd ps ->
Subset parties ps ->
Circuit parties ->
Choreo ps (CLI m) (Faceted parties '[] Bool)
computeWire trustedAnd parties circuit = case circuit of
InputWire p -> do
value <- inSuper parties p `_locally` getInput "Enter a secret input value:"
secretShare parties (inSuper parties p) (singleton, value)
LitWire b -> do
let shares = partyNames `zip` (b : repeat False)
fanOut \p -> inSuper parties p `_locally` return (fromJust $ toLocTm p `lookup` shares)
AndGate l r -> do
lResult <- compute l
rResult <- compute r
inputShares <- fanIn (trustedAnd @@ nobody) \p -> do
(inSuper parties p, \un -> return (viewFacet un p lResult, viewFacet un p rResult)) ~~> trustedAnd @@ nobody
outputVal <-
(trustedAnd @@ nobody) `congruently` \un ->
let ovs = un refl inputShares
in xor (fst <$> ovs) && xor (snd <$> ovs)
secretShare parties trustedAnd (singleton, outputVal)
XorGate l r -> do
lResult <- compute l
rResult <- compute r
parties `parallel` \p un -> pure (viewFacet un p lResult /= viewFacet un p rResult)
where
compute = computeWire trustedAnd parties
partyNames = toLocs parties
mpc ::
(KnownSymbols parties, MonadIO m) =>
Circuit parties ->
Choreo ("trusted3rdParty" ': parties) (CLI m) ()
mpc circuit = do
let parties = consSuper refl
outputWire <- computeWire trusted3rdParty parties circuit
result <- enclave parties $ reveal outputWire
parties `parallel_` \p un -> putOutput "The resulting bit:" $ un p result