module Tendermint.SDK.Application.AnteHandler
  ( module Tendermint.SDK.Application.AnteHandler
  --  Re-Exports
  , AnteHandler
  ) where

import           Control.Monad                      (unless, void)
import           Data.Foldable                      (fold)
import           Data.Monoid                        (Endo (..))
import           Polysemy
import           Polysemy.Error                     (Error)
import           Tendermint.SDK.BaseApp.Errors      (AppError, SDKError (..),
                                                     throwSDKError)
import           Tendermint.SDK.BaseApp.Transaction (AnteHandler,
                                                     RoutingTx (..))
import qualified Tendermint.SDK.Modules.Auth        as A
import           Tendermint.SDK.Types.Message       (Msg (..))
import           Tendermint.SDK.Types.Transaction   (Tx (..))


createAccountAnteHandler
  :: Members A.AuthEffs r
  => AnteHandler r
createAccountAnteHandler :: AnteHandler r
createAccountAnteHandler = ((RoutingTx msg -> Sem r a) -> RoutingTx msg -> Sem r a)
-> Endo (RoutingTx msg -> Sem r a)
forall a. (a -> a) -> Endo a
Endo (((RoutingTx msg -> Sem r a) -> RoutingTx msg -> Sem r a)
 -> Endo (RoutingTx msg -> Sem r a))
-> ((RoutingTx msg -> Sem r a) -> RoutingTx msg -> Sem r a)
-> Endo (RoutingTx msg -> Sem r a)
forall a b. (a -> b) -> a -> b
$
  \txApplication :: RoutingTx msg -> Sem r a
txApplication tx :: RoutingTx msg
tx@(RoutingTx Tx{..}) -> do
    let Msg{Address
msgAuthor :: forall msg. Msg msg -> Address
msgAuthor :: Address
msgAuthor} = Msg msg
txMsg
    Maybe Account
mAcnt <- Address -> Sem r (Maybe Account)
forall (r :: [Effect]).
MemberWithError Accounts r =>
Address -> Sem r (Maybe Account)
A.getAccount Address
msgAuthor
    case Maybe Account
mAcnt of
      Nothing -> Sem r Account -> Sem r ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Sem r Account -> Sem r ()) -> Sem r Account -> Sem r ()
forall a b. (a -> b) -> a -> b
$ Address -> Sem r Account
forall (r :: [Effect]).
MemberWithError Accounts r =>
Address -> Sem r Account
A.createAccount Address
msgAuthor
      _       -> () -> Sem r ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    RoutingTx msg -> Sem r a
txApplication RoutingTx msg
tx Sem r a -> (a -> Sem r a) -> Sem r a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure

nonceAnteHandler
  :: Members A.AuthEffs r
  => Member (Error AppError) r
  => AnteHandler r
nonceAnteHandler :: AnteHandler r
nonceAnteHandler = ((RoutingTx msg -> Sem r a) -> RoutingTx msg -> Sem r a)
-> Endo (RoutingTx msg -> Sem r a)
forall a. (a -> a) -> Endo a
Endo (((RoutingTx msg -> Sem r a) -> RoutingTx msg -> Sem r a)
 -> Endo (RoutingTx msg -> Sem r a))
-> ((RoutingTx msg -> Sem r a) -> RoutingTx msg -> Sem r a)
-> Endo (RoutingTx msg -> Sem r a)
forall a b. (a -> b) -> a -> b
$
  \txApplication :: RoutingTx msg -> Sem r a
txApplication tx :: RoutingTx msg
tx@(RoutingTx Tx{..}) -> do
    let Msg{Address
msgAuthor :: Address
msgAuthor :: forall msg. Msg msg -> Address
msgAuthor} = Msg msg
txMsg
    Maybe Account
preMAcnt <- Address -> Sem r (Maybe Account)
forall (r :: [Effect]).
MemberWithError Accounts r =>
Address -> Sem r (Maybe Account)
A.getAccount Address
msgAuthor
    case Maybe Account
preMAcnt of
      Just A.Account{Word64
accountNonce :: Account -> Word64
accountNonce :: Word64
accountNonce} -> do
        let expectedNonce :: Word64
expectedNonce = Word64
accountNonce Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1
        Bool -> Sem r () -> Sem r ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Word64
txNonce Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
expectedNonce) (Sem r () -> Sem r ()) -> Sem r () -> Sem r ()
forall a b. (a -> b) -> a -> b
$
          SDKError -> Sem r ()
forall (r :: [Effect]) a.
Member (Error AppError) r =>
SDKError -> Sem r a
throwSDKError (Word64 -> Word64 -> SDKError
NonceException Word64
expectedNonce Word64
txNonce)
      Nothing -> SDKError -> Sem r ()
forall (r :: [Effect]) a.
Member (Error AppError) r =>
SDKError -> Sem r a
throwSDKError (Address -> SDKError
UnknownAccountError Address
msgAuthor)
    a
result <- RoutingTx msg -> Sem r a
txApplication RoutingTx msg
tx
    Maybe Account
postMAcnt <- Address -> Sem r (Maybe Account)
forall (r :: [Effect]).
MemberWithError Accounts r =>
Address -> Sem r (Maybe Account)
A.getAccount Address
msgAuthor
    case Maybe Account
postMAcnt of
      Just A.Account{Word64
accountNonce :: Word64
accountNonce :: Account -> Word64
accountNonce} -> do
        Address -> (Account -> Account) -> Sem r ()
forall (r :: [Effect]).
MemberWithError Accounts r =>
Address -> (Account -> Account) -> Sem r ()
A.updateAccount Address
msgAuthor ((Account -> Account) -> Sem r ())
-> (Account -> Account) -> Sem r ()
forall a b. (a -> b) -> a -> b
$ \a :: Account
a ->
          Account
a { accountNonce :: Word64
A.accountNonce = Word64
accountNonce Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1}
      -- @NOTE: no-op when no nonce is availble to update
      Nothing -> () -> Sem r ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
result

baseAppAnteHandler
  :: Members A.AuthEffs r
  => Member (Error AppError) r
  => AnteHandler r
baseAppAnteHandler :: AnteHandler r
baseAppAnteHandler = [Endo (RoutingTx msg -> Sem r a)]
-> Endo (RoutingTx msg -> Sem r a)
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold ([Endo (RoutingTx msg -> Sem r a)]
 -> Endo (RoutingTx msg -> Sem r a))
-> [Endo (RoutingTx msg -> Sem r a)]
-> Endo (RoutingTx msg -> Sem r a)
forall a b. (a -> b) -> a -> b
$
  -- @NOTE: antehandlers in this list are applied top to bottom
  [ Endo (RoutingTx msg -> Sem r a)
forall (r :: [Effect]). Members AuthEffs r => AnteHandler r
createAccountAnteHandler
  , Endo (RoutingTx msg -> Sem r a)
forall (r :: [Effect]).
(Members AuthEffs r, Member (Error AppError) r) =>
AnteHandler r
nonceAnteHandler
  ]