-- 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
  , revealKeyOp
  , revealKeyUnlessRevealedOp
  ) 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.TezosClient.Impl (getAlias)
import Morley.Client.Types
import Morley.Tezos.Address
import Morley.Tezos.Address.Alias (AddressOrAlias(..))
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

-- | 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.
revealKeyOp
-- TODO [#873] remove HasTezosClient dependency
  :: forall m env.
     ( HasTezosRpc m
     , HasTezosClient m
     , WithClientLog env m
     )
  => PublicKey
  -> Maybe Mutez
  -> m OperationHash
revealKeyOp :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
PublicKey -> Maybe Mutez -> m OperationHash
revealKeyOp PublicKey
pk Maybe Mutez
mbFee = do
  Alias 'AddressKindImplicit
senderAlias <- AddressOrAlias 'AddressKindImplicit
-> m (ResolvedAlias (AddressOrAlias 'AddressKindImplicit))
forall addressOrAlias (m :: * -> *) env.
(HasTezosClient m, WithClientLog env m, MonadThrow m,
 Resolve addressOrAlias) =>
addressOrAlias -> m (ResolvedAlias addressOrAlias)
getAlias (AddressOrAlias 'AddressKindImplicit
 -> m (ResolvedAlias (AddressOrAlias 'AddressKindImplicit)))
-> AddressOrAlias 'AddressKindImplicit
-> m (ResolvedAlias (AddressOrAlias 'AddressKindImplicit))
forall a b. (a -> b) -> a -> b
$ KindedAddress 'AddressKindImplicit
-> AddressOrAlias 'AddressKindImplicit
forall (kind :: AddressKind).
L1AddressKind kind =>
KindedAddress kind -> AddressOrAlias kind
AddressResolved KindedAddress 'AddressKindImplicit
senderAddress
  ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressWithAlias
-> PublicKey -> Maybe Mutez -> m OperationHash
runRevealOperationRaw (KindedAddress 'AddressKindImplicit
-> Alias 'AddressKindImplicit -> ImplicitAddressWithAlias
forall (kind :: AddressKind).
KindedAddress kind -> Alias kind -> AddressWithAlias kind
AddressWithAlias KindedAddress 'AddressKindImplicit
senderAddress Alias 'AddressKindImplicit
senderAlias) PublicKey
pk Maybe Mutez
mbFee
  where
    senderAddress :: KindedAddress 'AddressKindImplicit
senderAddress = PublicKey -> KindedAddress 'AddressKindImplicit
mkKeyAddress PublicKey
pk
{-# DEPRECATED revealKeyOp "Prefer using 'revealKeyWithFee', as it's a lot more efficient" #-}

-- | Reveal given key.
--
-- Note that sender is implicitly defined by the key being revealed, as you can
-- only reveal your own key.
revealKeyUnlessRevealedOp
-- TODO [#873] remove HasTezosClient dependency
  :: forall m env.
     ( HasTezosRpc m
     , HasTezosClient m
     , WithClientLog env m
     )
  => PublicKey
  -> Maybe Mutez
  -> m ()
revealKeyUnlessRevealedOp :: forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
PublicKey -> Maybe Mutez -> m ()
revealKeyUnlessRevealedOp PublicKey
key Maybe Mutez
mbFee = (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 PublicKey -> Maybe Mutez -> m OperationHash
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
PublicKey -> Maybe Mutez -> m OperationHash
revealKeyOp Maybe Mutez
mbFee) PublicKey
key
{-# DEPRECATED revealKeyUnlessRevealedOp
  "Prefer using 'revealKeyUnlessRevealedWithFee', as it's a lot more efficient" #-}

-- 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 :: KindedAddress 'AddressKindImplicit
sender = PublicKey -> KindedAddress 'AddressKindImplicit
mkKeyAddress PublicKey
key
  -- An optimization for the average case, but we can't rely on it in
  -- distributed environment
  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 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 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

-- | 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 (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
. 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
..}