Skip to content

Commit 02aa311

Browse files
committed
[cli] Have an oracle service
1 parent af831e0 commit 02aa311

File tree

9 files changed

+213
-1
lines changed

9 files changed

+213
-1
lines changed

cli/anti.cabal

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ library
117117
Oracle.Config.Options
118118
Oracle.Config.Types
119119
Oracle.Options
120+
Oracle.Process
120121
Oracle.Token.Cli
121122
Oracle.Token.Options
122123
Oracle.Types
@@ -222,6 +223,29 @@ executable anti
222223
-- Base language which the package is written in.
223224
default-language: Haskell2010
224225

226+
executable anti-oracle
227+
-- Import common warning flags.
228+
import: warnings
229+
230+
-- .hs or .lhs file containing the Main module.
231+
main-is: app/anti-oracle.hs
232+
233+
-- Modules included in this executable, other than Main.
234+
-- other-modules:
235+
236+
-- LANGUAGE extensions used by modules in this package.
237+
-- other-extensions:
238+
239+
-- Other library packages from which modules are imported.
240+
build-depends:
241+
, anti
242+
, base
243+
244+
-- Directories containing source files.
245+
246+
-- Base language which the package is written in.
247+
default-language: Haskell2010
248+
225249
test-suite anti-E2E-test
226250
-- Import common warning flags.
227251
import: warnings

cli/app/anti-oracle.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Oracle.Process (oracleProcess, parseArgs)
2+
3+
main :: IO ()
4+
main = parseArgs >>= oracleProcess

cli/nix/anti-project.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ in {
6262
devShells.default = project.shell;
6363
inherit project;
6464
packages.anti = project.hsPkgs.anti.components.exes.anti;
65+
packages.anti-oracle = project.hsPkgs.anti.components.exes.anti-oracle;
6566
packages.bech32 = project.hsPkgs.bech32.components.exes.bech32;
6667
packages.cardano-address =
6768
project.hsPkgs.cardano-addresses.components.exes.cardano-address;

cli/src/Cli.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Cli
22
( cmd
33
, Command (..)
4+
, WithValidation (..)
45
) where
56

67
import Control.Monad.IO.Class (MonadIO (..))

cli/src/Core/Context.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module Core.Context
1313
, askConfig
1414
) where
1515

16+
import Control.Monad.IO.Class (MonadIO)
1617
import Control.Monad.Trans.Class (MonadTrans, lift)
1718
import Control.Monad.Trans.Reader (ReaderT (..), ask)
1819
import Core.Types.Basic (Owner, TokenId)
@@ -40,7 +41,7 @@ data Context m = Context
4041
newtype WithContext m a = WithContext
4142
{ _getWithContext :: ReaderT (Context m) m a
4243
}
43-
deriving (Functor, Applicative, Monad)
44+
deriving (Functor, Applicative, Monad, MonadIO)
4445

4546
instance MonadTrans WithContext where
4647
lift = WithContext . lift

cli/src/Core/Types/MPFS.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
module Core.Types.MPFS
44
( MPFSClient (..)
5+
, newClient
56
, mpfsClientOption
67
)
78
where

cli/src/Options.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Options
1010
, Options (..)
1111
, optionsParser
1212
, parseArgs
13+
, githubAuthOption
1314
) where
1415

1516
import Cli (Command (..))

cli/src/Oracle/Process.hs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
{-
2+
This is a full automated oracle process that can be used in the CLI.
3+
The process
4+
- respects a maximum number of requests to process in one run.
5+
- poll the MPFS system to get new requests. (get token)
6+
- batches requests and calls MPFS to include them in the token state.
7+
-}
8+
9+
module Oracle.Process
10+
( ProcessOptions (..)
11+
, oracleProcess
12+
, parseArgs
13+
) where
14+
15+
import Cli (Command (GetToken), WithValidation (..), cmd)
16+
import Control.Concurrent (threadDelay)
17+
import Control.Monad (forM_)
18+
import Control.Monad.Catch (MonadMask)
19+
import Control.Monad.IO.Class (MonadIO (..))
20+
import Core.Context (WithContext, withContext)
21+
import Core.Options (tokenIdOption, walletOption)
22+
import Core.Types.Basic (RequestRefId, TokenId)
23+
import Core.Types.MPFS (MPFSClient (..), mpfsClientOption)
24+
import Core.Types.Wallet (Wallet)
25+
import Data.Functor (void)
26+
import GitHub (Auth)
27+
import MPFS.API (mpfsClient)
28+
import OptEnvConf
29+
import Options (githubAuthOption)
30+
import Oracle.Token.Cli
31+
( TokenCommand (..)
32+
, TokenUpdateFailure
33+
, tokenCmdCore
34+
)
35+
import Oracle.Types
36+
( Token (tokenRequests)
37+
, requestZooRefId
38+
)
39+
import Oracle.Validate.Types (AValidationResult (..), Validated (..))
40+
import Paths_anti (version)
41+
import Validation (mkValidation)
42+
43+
parseArgs :: IO ProcessOptions
44+
parseArgs =
45+
runParser
46+
version
47+
"anithesis-oracle-process - Automated antithesis oracle process"
48+
processOptionsParser
49+
50+
data ProcessOptions = ProcessOptions
51+
{ poAuth :: Auth
52+
, poMaxRequestsPerBatch :: Int
53+
, poPollIntervalSeconds :: Int
54+
, poWallet :: Wallet
55+
, poTokenId :: TokenId
56+
, poMPFSClient :: MPFSClient
57+
}
58+
59+
processOptionsParser :: Parser ProcessOptions
60+
processOptionsParser =
61+
ProcessOptions
62+
<$> githubAuthOption
63+
<*> maxRequestsPerBatchOption
64+
<*> pollIntervalOption
65+
<*> walletOption
66+
<*> tokenIdOption
67+
<*> mpfsClientOption
68+
69+
maxRequestsPerBatchOption :: Parser Int
70+
maxRequestsPerBatchOption =
71+
setting
72+
[ long "max-requests-per-batch"
73+
, metavar "INT"
74+
, help "Maximum number of requests to include in one batch"
75+
, env "MAX_REQUESTS_PER_BATCH"
76+
, reader auto
77+
, option
78+
, value 5
79+
]
80+
81+
pollIntervalOption :: Parser Int
82+
pollIntervalOption =
83+
setting
84+
[ long "poll-interval"
85+
, metavar "SECONDS"
86+
, help "Interval in seconds between polling for new requests"
87+
, env "POLL_INTERVAL_SECONDS"
88+
, reader auto
89+
, option
90+
, value 30
91+
]
92+
93+
oracleProcess :: ProcessOptions -> IO ()
94+
oracleProcess = \case
95+
opts@ProcessOptions{poAuth, poMPFSClient} -> do
96+
let MPFSClient{runMPFS, submitTx} = poMPFSClient
97+
runMPFS
98+
$ withContext
99+
mpfsClient
100+
(mkValidation poAuth)
101+
submitTx
102+
$ processServer opts
103+
104+
processServer
105+
:: forall m
106+
. (MonadIO m, MonadMask m)
107+
=> ProcessOptions
108+
-> WithContext m ()
109+
processServer opts@ProcessOptions{poPollIntervalSeconds} = do
110+
liftIO $ putStrLn "Starting oracle process server..."
111+
let loop :: WithContext m () = do
112+
liftIO $ putStrLn "Polling for new requests..."
113+
reqIds <- liftIO $ poll opts
114+
if null reqIds
115+
then do
116+
liftIO
117+
$ putStrLn
118+
$ "No new requests found. Sleeping for "
119+
++ show poPollIntervalSeconds
120+
++ " seconds..."
121+
liftIO $ threadDelay (poPollIntervalSeconds * 1000000)
122+
loop
123+
else do
124+
liftIO
125+
$ putStrLn
126+
$ "Found "
127+
++ show (length reqIds)
128+
++ " new requests. Processing..."
129+
let batches = batch opts reqIds
130+
forM_ batches $ \batchReqIds -> do
131+
liftIO
132+
$ putStrLn
133+
$ "Submitting batch of "
134+
++ show (length batchReqIds)
135+
++ " requests..."
136+
result <- submit opts batchReqIds
137+
case result of
138+
ValidationFailure err ->
139+
liftIO
140+
$ putStrLn
141+
$ "Failed to submit batch: " ++ show err
142+
ValidationSuccess txHash ->
143+
liftIO
144+
$ putStrLn
145+
$ "Successfully submitted batch with tx hash: "
146+
++ show txHash
147+
loop
148+
loop
149+
poll :: ProcessOptions -> IO [RequestRefId]
150+
poll ProcessOptions{poTokenId, poMPFSClient, poAuth} = do
151+
result <- cmd (GetToken poAuth poMPFSClient poTokenId)
152+
case result of
153+
ValidationFailure err -> error $ "Failed to get token: " ++ show err
154+
ValidationSuccess token -> pure
155+
$ fmap (requestZooRefId . request)
156+
$ flip filter (tokenRequests token)
157+
$ \(WithValidation v _) -> case v of
158+
ValidationFailure _err -> False
159+
ValidationSuccess Validated -> True
160+
161+
batch :: ProcessOptions -> [RequestRefId] -> [[RequestRefId]]
162+
batch ProcessOptions{poMaxRequestsPerBatch} = go
163+
where
164+
go [] = []
165+
go xs = take poMaxRequestsPerBatch xs : go (drop poMaxRequestsPerBatch xs)
166+
167+
submit
168+
:: (MonadIO m, MonadMask m)
169+
=> ProcessOptions
170+
-> [RequestRefId]
171+
-> WithContext
172+
m
173+
( AValidationResult
174+
TokenUpdateFailure
175+
()
176+
)
177+
submit ProcessOptions{poWallet, poTokenId} reqIds = do
178+
void <$> tokenCmdCore (UpdateToken poTokenId poWallet reqIds)

cli/src/Oracle/Token/Cli.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Oracle.Token.Cli
22
( tokenCmdCore
33
, TokenCommand (..)
4+
, TokenUpdateFailure (..)
45
) where
56

67
import Control.Exception (Exception)

0 commit comments

Comments
 (0)