{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- |
-- Module      :  Network.Polkadot.Extrinsic.Unchecked
-- Copyright   :  Aleksandr Krupenkin 2016-2024
-- License     :  Apache-2.0
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  unportable
--
-- Extrinsic is a piece of data from external world.
--

module Network.Polkadot.Extrinsic.Unchecked
    ( UncheckedExtrinsic (..)
    , new_unsigned
    , new_signed
    , sign_extrinsic
    ) where

import           Codec.Scale                                (encode)
import           Codec.Scale.Class                          (Decode (..),
                                                             Encode (..))
import           Control.Arrow                              ((&&&))
import           Control.Monad                              (when)
import           Data.Bits                                  (clearBit, setBit,
                                                             testBit)
import           Data.ByteArray.HexString                   (HexString)
import           Data.ByteString                            (ByteString)
import           Data.Word                                  (Word8)
import           Network.JsonRpc.TinyClient                 (JsonRpc)

import           Network.Polkadot.Crypto                    (MultiPair (..))
import           Network.Polkadot.Extrinsic.Payload         (sign_payload)
import           Network.Polkadot.Extrinsic.SignedExtension (SignedExtension)

-- | Current version of the 'UncheckedExtrinsic' format.
extrinsic_version :: Word8
extrinsic_version :: Word8
extrinsic_version = Word8
4

-- | A extrinsic right from the external world. This is unchecked and so
-- can contain a signature.
data UncheckedExtrinsic c a s e
    = UncheckedExtrinsic
    { forall c a s e. UncheckedExtrinsic c a s e -> Maybe (a, s, e)
extrinsicSignature :: !(Maybe (a, s, e))
      -- ^ The signature, address, number of extrinsics have come before from
      -- the same signer and an era describing the longevity of this transaction,
      -- if this is a signed extrinsic.
    , forall c a s e. UncheckedExtrinsic c a s e -> c
extrinsicFunction  :: !c
      -- ^ The function that should be called.
    }

instance Encode c => Show (UncheckedExtrinsic c a b c) where
    show :: UncheckedExtrinsic c a b c -> String
show (UncheckedExtrinsic Maybe (a, b, c)
_ c
call) = String
"UncheckedExtrinsic " String -> ShowS
forall a. [a] -> [a] -> [a]
++ HexString -> String
forall a. Show a => a -> String
show HexString
encoded
      where
        encoded :: HexString
        encoded :: HexString
encoded = c -> HexString
forall a ba. (Encode a, ByteArray ba) => a -> ba
encode c
call

instance (Encode c, Encode a, Encode s, Encode e) => Encode (UncheckedExtrinsic c a s e) where
    put :: Putter (UncheckedExtrinsic c a s e)
put UncheckedExtrinsic c a s e
xt = Putter ByteString
forall a. Encode a => Putter a
put ByteString
encoded
      where
        encoded :: ByteString
        encoded :: ByteString
encoded = case UncheckedExtrinsic c a s e
xt of
            UncheckedExtrinsic Maybe (a, s, e)
Nothing c
call
                -> Word8 -> ByteString
forall a ba. (Encode a, ByteArray ba) => a -> ba
encode Word8
extrinsic_version ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> c -> ByteString
forall a ba. (Encode a, ByteArray ba) => a -> ba
encode c
call
            UncheckedExtrinsic (Just (a, s, e)
s) c
call
                -> Word8 -> ByteString
forall a ba. (Encode a, ByteArray ba) => a -> ba
encode (Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
setBit Word8
extrinsic_version Int
7) ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> (a, s, e) -> ByteString
forall a ba. (Encode a, ByteArray ba) => a -> ba
encode (a, s, e)
s ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> c -> ByteString
forall a ba. (Encode a, ByteArray ba) => a -> ba
encode c
call

instance (Decode c, Decode a, Decode s, Decode e) => Decode (UncheckedExtrinsic c a s e) where
    get :: Get (UncheckedExtrinsic c a s e)
get = do
        ([()]
_v :: [()]) <- Get [()]
forall a. Decode a => Get a
get
        (Bool
signed, Word8
version) <- ((Word8 -> Int -> Bool) -> Int -> Word8 -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Int
7 (Word8 -> Bool) -> (Word8 -> Word8) -> Word8 -> (Bool, Word8)
forall b c c'. (b -> c) -> (b -> c') -> b -> (c, c')
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& (Word8 -> Int -> Word8) -> Int -> Word8 -> Word8
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
clearBit Int
7) (Word8 -> (Bool, Word8)) -> Get Word8 -> Get (Bool, Word8)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word8
forall a. Decode a => Get a
get
        Bool -> Get () -> Get ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word8
version Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
extrinsic_version) (Get () -> Get ()) -> Get () -> Get ()
forall a b. (a -> b) -> a -> b
$ String -> Get ()
forall a. String -> Get a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"bad version"
        Maybe (a, s, e) -> c -> UncheckedExtrinsic c a s e
forall c a s e. Maybe (a, s, e) -> c -> UncheckedExtrinsic c a s e
UncheckedExtrinsic
            (Maybe (a, s, e) -> c -> UncheckedExtrinsic c a s e)
-> Get (Maybe (a, s, e)) -> Get (c -> UncheckedExtrinsic c a s e)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (if Bool
signed then ((a, s, e) -> Maybe (a, s, e))
-> Get (a, s, e) -> Get (Maybe (a, s, e))
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (a, s, e) -> Maybe (a, s, e)
forall a. a -> Maybe a
Just Get (a, s, e)
forall a. Decode a => Get a
get else Maybe (a, s, e) -> Get (Maybe (a, s, e))
forall a. a -> Get a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (a, s, e)
forall a. Maybe a
Nothing)
            Get (c -> UncheckedExtrinsic c a s e)
-> Get c -> Get (UncheckedExtrinsic c a s e)
forall a b. Get (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get c
forall a. Decode a => Get a
get

-- | New instance of a signed extrinsic aka "transaction".
new_signed :: c -> a -> s -> e -> UncheckedExtrinsic c a s e
new_signed :: forall c a s e. c -> a -> s -> e -> UncheckedExtrinsic c a s e
new_signed c
call a
address s
sig e
extra = Maybe (a, s, e) -> c -> UncheckedExtrinsic c a s e
forall c a s e. Maybe (a, s, e) -> c -> UncheckedExtrinsic c a s e
UncheckedExtrinsic ((a, s, e) -> Maybe (a, s, e)
forall a. a -> Maybe a
Just (a
address, s
sig, e
extra)) c
call

-- | New instance of an unsigned extrinsic aka "inherent".
new_unsigned :: f -> UncheckedExtrinsic f a b c
new_unsigned :: forall f a b c. f -> UncheckedExtrinsic f a b c
new_unsigned = Maybe (a, b, c) -> f -> UncheckedExtrinsic f a b c
forall c a s e. Maybe (a, s, e) -> c -> UncheckedExtrinsic c a s e
UncheckedExtrinsic Maybe (a, b, c)
forall a. Maybe a
Nothing

-- | Create and sign extrinsic by account.
sign_extrinsic :: (MultiPair a, Encode c, SignedExtension e, JsonRpc m)
               => a
               -- ^ Account to sign extrinsic.
               -> c
               -- ^ Function to call on runtime.
               -> e
               -- ^ Additional data to sign like nonce, blockhash, etc.
               -> m (UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e)
sign_extrinsic :: forall a c e (m :: * -> *).
(MultiPair a, Encode c, SignedExtension e, JsonRpc m) =>
a
-> c
-> e
-> m (UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e)
sign_extrinsic a
a c
c e
e = do
    MultiSignature a
sig <- a -> Payload c e -> m (MultiSignature a)
forall a c e (m :: * -> *).
(MultiPair a, Encode c, SignedExtension e, JsonRpc m) =>
a -> Payload c e -> m (MultiSignature a)
sign_payload a
a (c
c, e
e)
    UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e
-> m (UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e
 -> m (UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e))
-> UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e
-> m (UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e)
forall a b. (a -> b) -> a -> b
$ c
-> MultiAddress a
-> MultiSignature a
-> e
-> UncheckedExtrinsic c (MultiAddress a) (MultiSignature a) e
forall c a s e. c -> a -> s -> e -> UncheckedExtrinsic c a s e
new_signed c
c (a -> MultiAddress a
forall a. MultiPair a => a -> MultiAddress a
multi_address a
a) MultiSignature a
sig e
e