-- SPDX-FileCopyrightText: 2022 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

-- | Functions to reveal keys via node RPC.
module Morley.Client.Action.Reveal
  ( RevealData (..)
  , revealKey
  , revealKeyUnlessRevealed
  ) where

import Fmt ((|+))

import Morley.Client.Action.Common hiding (revealKeyUnlessRevealed)
import Morley.Client.Action.Operation
import Morley.Client.Logging
import Morley.Client.RPC.Class
import Morley.Client.RPC.Error
import Morley.Client.RPC.Getters
import Morley.Client.RPC.Types
import Morley.Client.TezosClient (HasTezosClient)
import Morley.Client.Types
import Morley.Tezos.Address (mkKeyAddress)
import Morley.Tezos.Address.Alias (AddressOrAlias(..))
import Morley.Tezos.Core (Mutez)
import Morley.Tezos.Crypto (PublicKey)

-- | Reveal given key.
--
-- Note that sender is implicitly defined by the key being revealed, as you can
-- only reveal your own key.
--
-- This is a variation of key revealing method that tries to use solely RPC.
revealKey
-- TODO [#873] remove HasTezosClient dependency
  :: forall m env.
     ( HasTezosRpc m
     , HasTezosClient m
     , WithClientLog env m
     )
  => PublicKey
  -> Maybe Mutez
  -> m OperationHash
revealKey :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
PublicKey -> Maybe Mutez -> m OperationHash
revealKey PublicKey
rdPublicKey Maybe Mutez
rdMbFee =
  ((OperationHash, NonEmpty (OperationInfo Result)) -> OperationHash)
-> m (OperationHash, NonEmpty (OperationInfo Result))
-> m OperationHash
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (OperationHash, NonEmpty (OperationInfo Result)) -> OperationHash
forall a b. (a, b) -> a
fst (m (OperationHash, NonEmpty (OperationInfo Result))
 -> m OperationHash)
-> (OperationInfo ClientInput
    -> m (OperationHash, NonEmpty (OperationInfo Result)))
-> OperationInfo ClientInput
-> m OperationHash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ImplicitAddressOrAlias
-> NonEmpty (OperationInfo ClientInput)
-> m (OperationHash, NonEmpty (OperationInfo Result))
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressOrAlias
-> NonEmpty (OperationInfo ClientInput)
-> m (OperationHash, NonEmpty (OperationInfo Result))
runOperationsNonEmpty (KindedAddress 'AddressKindImplicit -> ImplicitAddressOrAlias
forall (kind :: AddressKind).
L1AddressKind kind =>
KindedAddress kind -> AddressOrAlias kind
AddressResolved KindedAddress 'AddressKindImplicit
sender) (NonEmpty (OperationInfo ClientInput)
 -> m (OperationHash, NonEmpty (OperationInfo Result)))
-> (OperationInfo ClientInput
    -> NonEmpty (OperationInfo ClientInput))
-> OperationInfo ClientInput
-> m (OperationHash, NonEmpty (OperationInfo Result))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OperationInfo ClientInput -> NonEmpty (OperationInfo ClientInput)
forall x. One x => OneItem x -> x
one (OperationInfo ClientInput -> m OperationHash)
-> OperationInfo ClientInput -> m OperationHash
forall a b. (a -> b) -> a -> b
$ RevealInfo ClientInput -> OperationInfo ClientInput
forall i. RevealInfo i -> OperationInfo i
OpReveal RevealData :: PublicKey -> Maybe Mutez -> RevealData
RevealData{Maybe Mutez
PublicKey
rdMbFee :: Maybe Mutez
rdPublicKey :: PublicKey
rdMbFee :: Maybe Mutez
rdPublicKey :: PublicKey
..}
  where
    sender :: KindedAddress 'AddressKindImplicit
sender = PublicKey -> KindedAddress 'AddressKindImplicit
mkKeyAddress PublicKey
rdPublicKey

-- | Reveal given key.
--
-- Note that sender is implicitly defined by the key being revealed, as you can
-- only reveal your own key.
revealKeyUnlessRevealed
-- TODO [#873] remove HasTezosClient dependency
  :: forall m env.
     ( HasTezosRpc m
     , HasTezosClient m
     , WithClientLog env m
     )
  => PublicKey
  -> Maybe Mutez
  -> m ()
revealKeyUnlessRevealed :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
PublicKey -> Maybe Mutez -> m ()
revealKeyUnlessRevealed PublicKey
key Maybe Mutez
mbFee = do
  -- An optimization for the average case, but we can't rely on it in
  -- distributed environment
  let sender :: KindedAddress 'AddressKindImplicit
sender = PublicKey -> KindedAddress 'AddressKindImplicit
mkKeyAddress PublicKey
key
  KindedAddress 'AddressKindImplicit -> m (Maybe PublicKey)
forall (m :: * -> *).
HasTezosRpc m =>
KindedAddress 'AddressKindImplicit -> m (Maybe PublicKey)
getManagerKey KindedAddress 'AddressKindImplicit
sender m (Maybe PublicKey) -> (Maybe PublicKey -> m ()) -> m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Just PublicKey
_  -> Text -> m ()
forall env (m :: * -> *). WithLog env Message m => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ KindedAddress 'AddressKindImplicit
sender KindedAddress 'AddressKindImplicit -> Builder -> Text
forall a b. (Buildable a, FromBuilder b) => a -> Builder -> b
|+ Builder
" address has already revealed key"
    Maybe PublicKey
Nothing -> m () -> m ()
ignoreAlreadyRevealedError (m () -> m ())
-> (m OperationHash -> m ()) -> m OperationHash -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m OperationHash -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m OperationHash -> m ()) -> m OperationHash -> m ()
forall a b. (a -> b) -> a -> b
$ PublicKey -> Maybe Mutez -> m OperationHash
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
PublicKey -> Maybe Mutez -> m OperationHash
revealKey PublicKey
key Maybe Mutez
mbFee
  where
    ignoreAlreadyRevealedError :: m () -> m ()
ignoreAlreadyRevealedError = (m () -> (RunCodeErrors -> m ()) -> m ())
-> (RunCodeErrors -> m ()) -> m () -> m ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip m () -> (RunCodeErrors -> m ()) -> m ()
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> (e -> m a) -> m a
catch \case
      RunCodeErrors [PreviouslyRevealedKey KindedAddress 'AddressKindImplicit
_] -> m ()
forall (f :: * -> *). Applicative f => f ()
pass
      RunCodeErrors
e -> RunCodeErrors -> m ()
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM RunCodeErrors
e