<commit_before>-- | Pure functions for operations with transactions

module Pos.Wallet.Tx.Pure
       ( makePubKeyTx
       , createTx
       , getRelatedTxs
       , deriveAddrHistory
       , deriveAddrHistoryPartial
       , TxError
       ) where

import           Control.Lens              (over, use, uses, view, (%=), (%=), (.~), (^.),
                                            _1, _2)
import           Control.Monad.State       (StateT (..), evalStateT)
import           Control.Monad.Trans.Maybe (MaybeT (..))
import qualified Data.DList                as DL
import           Data.List                 (tail)
import qualified Data.Map                  as M
import qualified Data.Vector               as V
import           Universum

import           Pos.Binary                ()
import           Pos.Crypto                (SecretKey, WithHash (..), hash, sign,
                                            toPublic, withHash)
import           Pos.Data.Attributes       (mkAttributes)
import           Pos.Types                 (Address, Block, Coin, MonadUtxoRead (..),
                                            Tx (..), TxAux, TxDistribution (..), TxId,
                                            TxIn (..), TxInWitness (..), TxOut (..),
                                            TxOutAux, TxWitness, Utxo, UtxoStateT (..),
                                            applyTxToUtxo, blockTxas, filterUtxoByAddr,
                                            makePubKeyAddress, mkCoin, sumCoins,
                                            topsortTxs, _txOutputs)
import           Pos.Types.Coin            (unsafeIntegerToCoin, unsafeSubCoin)

type TxOutIdx = (TxId, Word32)
type TxInputs = [TxOutIdx]
type TxOutputs = [TxOutAux]
type TxError = Text

-----------------------------------------------------------------------------
-- Tx creation
-----------------------------------------------------------------------------

-- | Makes a transaction which use P2PKH addresses as a source
makePubKeyTx :: SecretKey -> TxInputs -> TxOutputs -> TxAux
makePubKeyTx sk inputs outputs = (Tx {..}, txWitness, txDist)
  where pk = toPublic sk
        txInputs = map makeTxIn inputs
        txOutputs = map fst outputs
        txAttributes = mkAttributes ()
        txOutHash = hash txOutputs
        txDist = TxDistribution (map snd outputs)
        txDistHash = hash txDist
        makeTxIn (txInHash, txInIndex) = TxIn {..}
        makeTxInWitness (txInHash, txInIndex) =
            PkWitness {
                twKey = pk,
                twSig = sign sk (txInHash, txInIndex, txOutHash, txDistHash) }
        txWitness = V.fromList (map makeTxInWitness inputs)

type FlatUtxo = [(TxOutIdx, TxOutAux)]
type InputPicker = StateT (Coin, FlatUtxo) (Either TxError)

-- | Make a multi-transaction using given secret key and info for outputs
createTx :: Utxo -> SecretKey -> TxOutputs -> Either TxError TxAux
createTx utxo sk outputs = uncurry (makePubKeyTx sk) <$> inpOuts
  where
    -- The total amount of money in the system is less than 2^64 so summing
    -- coins should be safe here
    totalMoney = unsafeIntegerToCoin $
                 sumCoins $ map (txOutValue . fst) outputs
    ourAddr = makePubKeyAddress $ toPublic sk
    allUnspent = M.toList $ filterUtxoByAddr ourAddr utxo
    sortedUnspent = sortBy (comparing $ Down . txOutValue . fst . snd) allUnspent
    inpOuts = do
        futxo <- evalStateT (pickInputs []) (totalMoney, sortedUnspent)
        let inputs = map fst futxo<commit_msg>[CSL-491] Abstract out general transaction making<commit_after>-- | Pure functions for operations with transactions

module Pos.Wallet.Tx.Pure
       ( makePubKeyTx
       , createTx
       , getRelatedTxs
       , deriveAddrHistory
       , deriveAddrHistoryPartial
       , TxError
       ) where

import           Control.Lens              (over, use, uses, view, (%=), (%=), (.~), (^.),
                                            _1, _2)
import           Control.Monad.State       (StateT (..), evalStateT)
import           Control.Monad.Trans.Maybe (MaybeT (..))
import qualified Data.DList                as DL
import           Data.List                 (tail)
import qualified Data.Map                  as M
import qualified Data.Vector               as V
import           Universum

import           Pos.Binary                ()
import           Pos.Crypto                (SecretKey, WithHash (..), hash, sign,
                                            toPublic, withHash)
import           Pos.Data.Attributes       (mkAttributes)
import           Pos.Types                 (Address, Block, Coin, MonadUtxoRead (..),
                                            Tx (..), TxAux, TxDistribution (..), TxId,
                                            TxIn (..), TxInWitness (..), TxInWitness,
                                            TxOut (..), TxOutAux, TxSigData, TxWitness,
                                            Utxo, UtxoStateT (..), applyTxToUtxo,
                                            blockTxas, filterUtxoByAddr,
                                            makePubKeyAddress, mkCoin, sumCoins,
                                            topsortTxs, _txOutputs)
import           Pos.Types.Coin            (unsafeIntegerToCoin, unsafeSubCoin)

type TxOutIdx = (TxId, Word32)
type TxInputs = [TxOutIdx]
type TxOutputs = [TxOutAux]
type TxError = Text

-----------------------------------------------------------------------------
-- Tx creation
-----------------------------------------------------------------------------

-- | Generic function to create a transaction, given desired inputs, outputs and a
-- way to construct witness from signature data
makeAbstractTx :: (TxSigData -> TxInWitness) -> TxInputs -> TxOutputs -> TxAux
makeAbstractTx mkWit inputs outputs = (Tx {..}, txWitness, txDist)
  where txInputs = map makeTxIn inputs
        txOutputs = map fst outputs
        txAttributes = mkAttributes ()
        txOutHash = hash txOutputs
        txDist = TxDistribution (map snd outputs)
        txDistHash = hash txDist
        txWitness = V.fromList $ map (mkWit . makeTxSigData) inputs
        makeTxIn (txInHash, txInIndex) = TxIn {..}
        makeTxSigData (txInHash, txInIndex) = (txInHash, txInIndex, txOutHash, txDistHash)

-- | Makes a transaction which use P2PKH addresses as a source
makePubKeyTx :: SecretKey -> TxInputs -> TxOutputs -> TxAux
makePubKeyTx sk = makeAbstractTx mkWit
  where pk = toPublic sk
        mkWit sigData = PkWitness
            { twKey = pk
            , twSig = sign sk sigData
            }

type FlatUtxo = [(TxOutIdx, TxOutAux)]
type InputPicker = StateT (Coin, FlatUtxo) (Either TxError)

-- | Make a multi-transaction using given secret key and info for outputs
createTx :: Utxo -> SecretKey -> TxOutputs -> Either TxError TxAux
createTx utxo sk outputs = uncurry (makePubKeyTx sk) <$> inpOuts
  where
    -- The total amount of money in the system is less than 2^64 so summing
    -- coins should be safe here
    totalMoney = unsafeIntegerToCoin $
                 sumCoins $ map (txOutValue . fst) outputs
    ourAddr = makePubKeyAddress $ toPublic sk
    allUnspent = M.toList $ filterUtxoByAddr ourAddr utxo
    sortedUnspent = sortBy (comparing $ Down . txOutValue . fst . snd) allUnspent
    inpOuts = do
        futxo <- evalStateT (pickInputs []) (totalMoney, sortedUnspent)
        let inputs = map fst futxo
{'fraction_non_alphanumeric': 0.12058268141354195, 'fraction_numerical': 0.0025627191799298624, 'mean_word_length': 1.532445355191257, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4089292', 'n_tokens_mistral': 2033, 'n_tokens_neox': 2000, 'n_words': 580}