-- |
-- Module      :  Network.Polkadot.Extrinsic.Era
-- Copyright   :  Aleksandr Krupenkin 2016-2024
-- License     :  Apache-2.0
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  unportable
--
-- An era to describe the longevity of a transaction.
--

module Network.Polkadot.Extrinsic.Era
    ( Era(..)
    , new_mortal_compact
    , birth
    , death
    ) where

import           Codec.Scale.Class (Decode (..), Encode (..))
import           Codec.Scale.Core  ()
import           Data.Bits         (shiftL, shiftR, (.|.))
import           Data.Word         (Word16, Word32, Word8, byteSwap16)

-- | The era for an extrinsic, indicating either a mortal or immortal extrinsic.
data Era
  = ImmortalEra
  -- ^ The ImmortalEra for an extrinsic.
  | MortalEra !Word32 !Word32
  -- ^ The MortalEra for an extrinsic, indicating period and phase.
  --
  -- Period and phase are encoded:
  -- - The period of validity from the block hash found in the signing material.
  -- - The phase in the period that this transaction's lifetime begins (and, importantly,
  -- implies which block hash is included in the signature material). If the `period` is
  -- greater than 1 << 12, then it will be a factor of the times greater than 1 << 12 that
  -- `period` is.
  deriving (Era -> Era -> Bool
(Era -> Era -> Bool) -> (Era -> Era -> Bool) -> Eq Era
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Era -> Era -> Bool
== :: Era -> Era -> Bool
$c/= :: Era -> Era -> Bool
/= :: Era -> Era -> Bool
Eq, Eq Era
Eq Era =>
(Era -> Era -> Ordering)
-> (Era -> Era -> Bool)
-> (Era -> Era -> Bool)
-> (Era -> Era -> Bool)
-> (Era -> Era -> Bool)
-> (Era -> Era -> Era)
-> (Era -> Era -> Era)
-> Ord Era
Era -> Era -> Bool
Era -> Era -> Ordering
Era -> Era -> Era
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Era -> Era -> Ordering
compare :: Era -> Era -> Ordering
$c< :: Era -> Era -> Bool
< :: Era -> Era -> Bool
$c<= :: Era -> Era -> Bool
<= :: Era -> Era -> Bool
$c> :: Era -> Era -> Bool
> :: Era -> Era -> Bool
$c>= :: Era -> Era -> Bool
>= :: Era -> Era -> Bool
$cmax :: Era -> Era -> Era
max :: Era -> Era -> Era
$cmin :: Era -> Era -> Era
min :: Era -> Era -> Era
Ord, Int -> Era -> ShowS
[Era] -> ShowS
Era -> String
(Int -> Era -> ShowS)
-> (Era -> String) -> ([Era] -> ShowS) -> Show Era
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Era -> ShowS
showsPrec :: Int -> Era -> ShowS
$cshow :: Era -> String
show :: Era -> String
$cshowList :: [Era] -> ShowS
showList :: [Era] -> ShowS
Show)

instance Decode Era where
    get :: Get Era
get = do
        Word8
first <- Get Word8
forall a. Decode a => Get a
get
        case Word8
first :: Word8 of
            Word8
0 -> Era -> Get Era
forall a. a -> Get a
forall (m :: * -> *) a. Monad m => a -> m a
return Era
ImmortalEra
            Word8
_ -> Word8 -> Word8 -> Era
decodeMortal Word8
first (Word8 -> Era) -> Get Word8 -> Get Era
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word8
forall a. Decode a => Get a
get
      where
        decodeMortal :: Word8 -> Word8 -> Era
        decodeMortal :: Word8 -> Word8 -> Era
decodeMortal Word8
first Word8
second =
            let first' :: Word16
first' = Word8 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
first
                second' :: Word16
second' = Word8 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
second
             in Word16 -> Era
new_mortal_compact (Word16
first' Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
+ Word16
second' Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftL` Int
8)

instance Encode Era where
    put :: Putter Era
put Era
ImmortalEra = Putter Word8
forall a. Encode a => Putter a
put (Word8
0 :: Word8)
    put (MortalEra Word32
period' Word32
phase') = Putter Word16
forall a. Encode a => Putter a
put Word16
encoded
      where
        encoded :: Word16
        encoded :: Word16
encoded = Word16
first Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Word16
second
        first :: Word16
first = (Word16
1 Word16 -> Word16 -> Word16
forall a. Ord a => a -> a -> a
`max` (Word16 -> Word16
forall a. Integral a => a -> a
trailing_zeros Word16
period Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
- Word16
1)) Word16 -> Word16 -> Word16
forall a. Ord a => a -> a -> a
`min` Word16
15
        second :: Word16
second = (Word16
phase Word16 -> Word16 -> Word16
forall a. Integral a => a -> a -> a
`div` Word16
quantizeFactor) Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftL` Int
4
        quantizeFactor :: Word16
quantizeFactor = Word16 -> Word16 -> Word16
forall a. Ord a => a -> a -> a
max (Word16
period Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftR` Int
12) Word16
1
        period :: Word16
period = Word32 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
period'
        phase :: Word16
phase = Word32 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
phase'

-- | Create a mortal 'Era' type from two bytes of data.
new_mortal_compact :: Word16 -> Era
new_mortal_compact :: Word16 -> Era
new_mortal_compact Word16
raw = Word32 -> Word32 -> Era
MortalEra Word32
period Word32
phase
  where
    era :: Word32
era = Word16 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word16 -> Word32) -> Word16 -> Word32
forall a b. (a -> b) -> a -> b
$ Word16 -> Word16
byteSwap16 Word16
raw

    period :: Word32
period = Word32
2 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` (Word32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
era Word32 -> Word32 -> Word32
forall a. Integral a => a -> a -> a
`rem` Word32
16))
    quantizeFactor :: Word32
quantizeFactor = Word32 -> Word32 -> Word32
forall a. Ord a => a -> a -> a
max (Word32
period Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
12) Word32
1
    phase :: Word32
phase = (Word32
era Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
4) Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
* Word32
quantizeFactor

trailing_zeros :: Integral a => a -> a
trailing_zeros :: forall a. Integral a => a -> a
trailing_zeros = (a -> a -> a) -> a -> [a] -> a
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl a -> a -> a
forall {a} {a}. (Integral a, Num a) => a -> a -> a
zero a
0 ([a] -> a) -> (a -> [a]) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Bool) -> [a] -> [a]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
0) ([a] -> [a]) -> (a -> [a]) -> a -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> a) -> a -> [a]
forall a. (a -> a) -> a -> [a]
iterate (a -> a -> a
forall a. Integral a => a -> a -> a
`div` a
2)
  where
    zero :: a -> a -> a
zero a
a a
x
      | a
x a -> a -> a
forall a. Integral a => a -> a -> a
`mod` a
2 a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
0 = a
a a -> a -> a
forall a. Num a => a -> a -> a
+ a
1
      | Bool
otherwise = a
a

-- | Get the block number of the start of the era whose properties this object
-- describes that `current` belongs to.
birth :: (Integral a, Integral b) => Era -> a -> b
birth :: forall a b. (Integral a, Integral b) => Era -> a -> b
birth Era
ImmortalEra a
_ = b
0
birth (MortalEra Word32
period Word32
phase) a
current = Word32 -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32 -> b) -> Word32 -> b
forall a b. (a -> b) -> a -> b
$
    (Word32 -> Word32 -> Word32
forall a. Ord a => a -> a -> a
max (a -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
current) Word32
phase Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
- Word32
phase) Word32 -> Word32 -> Word32
forall a. Integral a => a -> a -> a
`div` Word32
period Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
* Word32
period Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
+ Word32
phase

-- | Get the block number of the first block at which the era has ended.
death :: (Integral a, Integral b, Bounded b) => Era -> a -> b
death :: forall a b. (Integral a, Integral b, Bounded b) => Era -> a -> b
death Era
ImmortalEra a
_                  = b
forall a. Bounded a => a
maxBound
death e :: Era
e@(MortalEra Word32
period Word32
_) a
current = Word32 -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32 -> b) -> Word32 -> b
forall a b. (a -> b) -> a -> b
$ Era -> a -> Word32
forall a b. (Integral a, Integral b) => Era -> a -> b
birth Era
e a
current Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
+ Word32
period