-- 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
  , revealKeyWithFee
  , revealKeyUnlessRevealed
  , revealKeyUnlessRevealedWithFee
  ) where

import Fmt ((|+))

import Morley.Client.Action.Common
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.Class (HasTezosClient(getPublicKey))
import Morley.Client.Types
import Morley.Tezos.Address
import Morley.Tezos.Core (Mutez)
import Morley.Tezos.Crypto (PublicKey)

-- | Resolve the public key of an implicit address and reveal it.
revealKey
  :: (HasTezosRpc m, HasTezosClient m, WithClientLog env m)
  => ImplicitAddressWithAlias -> m OperationHash
revealKey :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias -> m OperationHash
revealKey = (ImplicitAddressWithAlias -> Maybe Mutez -> m OperationHash
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias -> Maybe Mutez -> m OperationHash
`revealKeyWithFee` Maybe Mutez
forall a. Maybe a
Nothing)

-- | Version of 'revealKey' with explicit fee.
revealKeyWithFee
  :: (HasTezosRpc m, HasTezosClient m, WithClientLog env m)
  => ImplicitAddressWithAlias -> Maybe Mutez -> m OperationHash
revealKeyWithFee :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias -> Maybe Mutez -> m OperationHash
revealKeyWithFee ImplicitAddressWithAlias
sender Maybe Mutez
mbFee = do
  PublicKey
pk <- ImplicitAddressWithAlias -> m PublicKey
forall (m :: * -> *).
HasTezosClient m =>
ImplicitAddressWithAlias -> m PublicKey
getPublicKey ImplicitAddressWithAlias
sender
  ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
runRevealOperationRaw ImplicitAddressWithAlias
sender PublicKey
pk Maybe Mutez
mbFee

-- | Resolve the public key of an implicit address and reveal it, unless already
-- revealed.
revealKeyUnlessRevealed
  :: (HasTezosRpc m, HasTezosClient m, WithClientLog env m)
  => ImplicitAddressWithAlias -> m ()
revealKeyUnlessRevealed :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias -> m ()
revealKeyUnlessRevealed = (ImplicitAddressWithAlias -> Maybe Mutez -> m ()
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias -> Maybe Mutez -> m ()
`revealKeyUnlessRevealedWithFee` Maybe Mutez
forall a. Maybe a
Nothing)

-- | Version of 'revealKeyUnlessRevealed' with explicit fee.
revealKeyUnlessRevealedWithFee
  :: (HasTezosRpc m, HasTezosClient m, WithClientLog env m)
  => ImplicitAddressWithAlias -> Maybe Mutez -> m ()
revealKeyUnlessRevealedWithFee :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias -> Maybe Mutez -> m ()
revealKeyUnlessRevealedWithFee ImplicitAddressWithAlias
sender Maybe Mutez
mbFee = do
  PublicKey
pk <- ImplicitAddressWithAlias -> m PublicKey
forall (m :: * -> *).
HasTezosClient m =>
ImplicitAddressWithAlias -> m PublicKey
getPublicKey ImplicitAddressWithAlias
sender
  (PublicKey -> m OperationHash) -> PublicKey -> m ()
forall (m :: * -> *) env a.
(HasTezosRpc m, WithClientLog env m) =>
(PublicKey -> m a) -> PublicKey -> m ()
handleAlreadyRevealed ((PublicKey -> Maybe Mutez -> m OperationHash)
-> Maybe Mutez -> PublicKey -> m OperationHash
forall a b c. (a -> b -> c) -> b -> a -> c
flip (ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
runRevealOperationRaw ImplicitAddressWithAlias
sender) Maybe Mutez
mbFee) PublicKey
pk

-- Internals

handleAlreadyRevealed
  :: (HasTezosRpc m, WithClientLog env m)
  => (PublicKey -> m a) -> PublicKey -> m ()
handleAlreadyRevealed :: forall (m :: * -> *) env a.
(HasTezosRpc m, WithClientLog env m) =>
(PublicKey -> m a) -> PublicKey -> m ()
handleAlreadyRevealed PublicKey -> m a
doReveal PublicKey
key = do
  let sender :: ImplicitAddress
sender = PublicKey -> ImplicitAddress
mkKeyAddress PublicKey
key
  -- An optimization for the average case, but we can't rely on it in
  -- distributed environment
  ImplicitAddress -> m (Maybe PublicKey)
forall (m :: * -> *).
HasTezosRpc m =>
ImplicitAddress -> m (Maybe PublicKey)
getManagerKey ImplicitAddress
sender m (Maybe PublicKey) -> (Maybe PublicKey -> m ()) -> m ()
forall a b. m a -> (a -> m b) -> m b
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
$ ImplicitAddress
sender ImplicitAddress -> Doc -> Text
forall a b. (Buildable a, FromDoc b) => a -> Doc -> b
|+ Doc
" address has already revealed key"
    Maybe PublicKey
Nothing -> m () -> m ()
ignoreAlreadyRevealedError (m () -> m ()) -> (m a -> m ()) -> m a -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m a -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m a -> m ()) -> m a -> m ()
forall a b. (a -> b) -> a -> b
$ PublicKey -> m a
doReveal PublicKey
key
  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 ImplicitAddress
_] -> 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

-- | Note that sender and rdPublicKey must be consistent, otherwise network will
-- reject the operation
runRevealOperationRaw
  :: (HasTezosRpc m, HasTezosClient m, WithClientLog env m)
  => ImplicitAddressWithAlias -> PublicKey -> Maybe Mutez -> m OperationHash
runRevealOperationRaw :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
runRevealOperationRaw ImplicitAddressWithAlias
sender PublicKey
rdPublicKey Maybe Mutez
rdMbFee =
  ((OperationHash, NonEmpty (OperationInfo Result)) -> OperationHash)
-> m (OperationHash, NonEmpty (OperationInfo Result))
-> m OperationHash
forall a b. (a -> b) -> m a -> m b
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
. ImplicitAddressWithAlias
-> NonEmpty (OperationInfo ClientInput)
-> m (OperationHash, NonEmpty (OperationInfo Result))
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias
-> NonEmpty (OperationInfo ClientInput)
-> m (OperationHash, NonEmpty (OperationInfo Result))
runOperationsNonEmpty ImplicitAddressWithAlias
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
. OneItem (NonEmpty (OperationInfo ClientInput))
-> NonEmpty (OperationInfo ClientInput)
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{Maybe Mutez
PublicKey
rdPublicKey :: PublicKey
rdMbFee :: Maybe Mutez
rdPublicKey :: PublicKey
rdMbFee :: Maybe Mutez
..}