packages feed

bitcoin-payment-channel-1.2.0.0: src/PaymentChannel/Internal/Payment/Verify.hs

module PaymentChannel.Internal.Payment.Verify
( paymentValueIncrease
, StateSignature
, _invalidBtcSig
)
where

import PaymentChannel.Internal.Payment.Types as Export
import PaymentChannel.Internal.Error.User
import PaymentChannel.Internal.Error.Internal         (ReceiverError(BadSignatureInState))
import Bitcoin.Compare

import Control.Exception                              (throw)
import Debug.Trace


-- | When we don't have a valid signature for the 'Payment' in the state
--    its signature data will be an 'InvalidSig'. Conversion
--    to a BtcSig is for backwards compatibility with 'payValIncrease' etc.
class StateSignature a where
    checkStateSig  :: Payment a -> Either VerifyError ()
    _btcSigPossiblyFake :: Payment a -> Payment BtcSig    -- ^ For backwards compatibilty with 'payValIncrease' (which doesn't look at signature data)

instance StateSignature BtcSig where
    checkStateSig = singlePairVerifySig 
    _btcSigPossiblyFake = id

instance StateSignature InvalidSig where
    checkStateSig = const $ Right () 
    _btcSigPossiblyFake = mapSigData _invalidBtcSig
        
-- | WARNING: Produces invalid 'BtcSig'
_invalidBtcSig :: InvalidSig -> BtcSig
_invalidBtcSig (MkInvalidSig sh) = BtcSig dummySig sh

-- | Throws 'BadSignatureInState' on invalid in-state payment signature
paymentValueIncrease :: 
       ( MonadTime m
       , StateSignature stateSigData
       ) =>
       PayChanState stateSigData  -- ^ State with old payment
    -> Payment BtcSig             -- ^ New payment
    -> m (Either PayChanError BtcAmount)
paymentValueIncrease state newPayment = do
    let settlePeriod = runConfM (pcsSettings state) confSettlePeriod
    fundingLocked <- fundingIsLocked (toSeconds settlePeriod) newPayment
    return $ 
        if fundingLocked 
            then checkedPayVal (pcsPayment state) newPayment
            else Left ChannelExpired
  where
    checkedPayVal statePayment payment = do
            valRecvd <- payValIncrease (_btcSigPossiblyFake statePayment) payment
            fmapL (const $ throw BadSignatureInState) (checkStateSig statePayment)
            _ <- fmapL (const SigVerifyFailed) (singlePairVerifySig payment)
            return valRecvd

payValIncrease ::
       Payment BtcSig    -- ^ Old, in-state payment
    -> Payment BtcSig    -- ^ New payment
    -> Either PayChanError BtcAmount
payValIncrease sp1 sp2 =
    comparePayments sp1 sp2 >>=
    \res -> case res of
        -- Inspect change in client change value.
        -- A decrease in client change value is an increase in value to us.
        DiffInfo [(_, Decrease val)] -> Right val
        DiffInfo [(_, NoChange)]     -> Right 0
        DiffInfo [(_, Increase val)] -> Left $ BadPaymentValue val
        DiffInfo x -> error $ "Not exactly one output in a 'Payment': " ++ show x

comparePayments ::
       Payment BtcSig
    -> Payment BtcSig
    -> Either PayChanError DiffInfo
comparePayments sp1 sp2 =
    fmapL PaymentError (valueDiff tx1 tx2) >>= eqIgnoreVal
  where
    (tx1,tx2) = (toBtcTx sp1, toBtcTx sp2)
    eqIgnoreVal di =
        if not $ eqIgnoreOutVal (IgnoreSigData tx1) (IgnoreSigData tx2)
            -- Means sig hash flags differ
            then Left $ show (tx1,tx2) `trace` BadSigHashFlag (bsSigFlag $ getSigData sp2) (bsSigFlag $ getSigData sp1)
            else Right di