{-# LANGUAGE DeriveGeneric #-}
module Bitcoin.SinglePair
(
module Bitcoin.SinglePair
, module X
)
where
import Bitcoin.Types as X
import Bitcoin.LockTime.Util as X
import Bitcoin.Signature as X
import Bitcoin.SpendCond.Cond as X
import Bitcoin.Tx
import qualified Network.Haskoin.Script as HS
import qualified Network.Haskoin.Crypto as HC
import qualified Data.List.NonEmpty as NE
import Control.Monad.Time
{-# ANN module ("HLint: ignore Use isNothing"::String) #-}
instance (Show r, Show sd) => IsTxLike SigSinglePair r sd where
toBtcTx SigSinglePair{..} =
BtcTx 1 inputL [singleOutput] Nothing Nothing
where inputL = singleInput NE.:| []
fromBtcTx tx@BtcTx{..}
| btcVer == 1 && btcLock == Nothing = SigSinglePair input output
| otherwise = error $ "Modified transaction data: " ++ show tx
where input = head . NE.toList $ btcIns
output = head btcOuts
mkSigSinglePair :: InputG r () -> BtcOut -> SigSinglePair r ()
mkSigSinglePair pin out =
SigSinglePair (adjustSignFlag pin) out
where
adjustSignFlag = setSignFlag $
if btcAmount out /= nullAmount
then HS.SigSingle True
else HS.SigNone True
signPair :: ( Monad m, Show t
, TransformSigData BtcSig () r, SignatureScript t BtcSig
, SpendFulfillment BtcSig r, HasSpendCond r t ) =>
HC.PrvKeyC
-> SigSinglePair t ()
-> m (Either BtcError (SigSinglePair t BtcSig))
signPair prvKey sp@SigSinglePair{..} =
fmap fromBtcTx <$> signTx (const $ return prvKey) (toBtcTx sp)
singlePairVerifySig ::
( Show ss, Show t
, SpendFulfillment ss r
, SpendCondition r
, HasSpendCond r t
) =>
SigSinglePair t ss -> Either VerifyError ()
singlePairVerifySig sp = verifyTx (toBtcTx sp)
toClientSignedTx :: Show t => NE.NonEmpty (SigSinglePair t BtcSig) -> BtcTx t BtcSig
toClientSignedTx spL = foldr addInOut (toBtcTx $ NE.last spL) (NE.init spL)
where addInOut SigSinglePair{..} tx@BtcTx{..} =
tx { btcIns = singleInput NE.<| btcIns
, btcOuts = singleOutput : btcOuts
}
getSigData :: SigSinglePair t sd -> sd
getSigData = btcSigData . singleInput
sigDataHash :: SigSinglePair t BtcSig -> HC.Hash256
sigDataHash = hashSigData . getSigData
pairRedeemScript :: HasSpendCond r t => SigSinglePair t a -> r
pairRedeemScript SigSinglePair{..} = inputCondScript singleInput
fundingValue :: SigSinglePair t BtcSig -> BtcAmount
fundingValue SigSinglePair{..} = btcInValue singleInput
clientChangeVal :: SigSinglePair t BtcSig -> BtcAmount
clientChangeVal SigSinglePair{..} = nonDusty (btcAmount singleOutput)
clientChangeAddr :: SigSinglePair t BtcSig -> HC.Address
clientChangeAddr SigSinglePair{..} = btcAddress singleOutput
clearSig :: SigSinglePair t BtcSig -> SigSinglePair t ()
clearSig = spMapSigData $ const ()
spMapSigData :: (a -> b) -> SigSinglePair t a -> SigSinglePair t b
spMapSigData f sp@SigSinglePair{..} = sp { singleInput = mapSigData f singleInput }
-- | After the lockTime -- specified in the funding output in the blockchain --
-- has passed, the client/sender can redeem all sent funds. So we make sure
-- this isn't possible before accepting a payment.
fundingIsLocked :: (Show t, Show sd, MonadTime m, HasLockTimeDate t) => SigSinglePair t sd -> m Bool
fundingIsLocked = allInputsLocked . toBtcTx