{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, DeriveGeneric, DeriveAnyClass #-} module PaymentChannel.Test ( module PaymentChannel.Test , module X ) where import PaymentChannel as X import PaymentChannel.Types as X import PaymentChannel.Util as X import PaymentChannel.Internal.Receiver.Types as X import PaymentChannel.Internal.ChanScript as X import qualified Network.Haskoin.Crypto as HC import Network.Haskoin.Test import Data.Time.Clock (UTCTime(..)) import Data.Time.Calendar (Day(..)) import Control.Monad (foldM) import Test.QuickCheck maxCoins :: Integer maxCoins = round $ (21e6 :: Double) * 1e8 mIN_CHANNEL_SIZE :: BtcAmount mIN_CHANNEL_SIZE = configDustLimit * 2 data ArbChannelPair = ArbChannelPair { sendChan :: ClientPayChan , recvChan :: ServerPayChan , initPayAmount :: BtcAmount , initRecvAmount :: BtcAmount , initPayment :: SignedPayment , recvPrvKey :: HC.PrvKeyC } deriving (Generic, NFData) data ChannelPairResult = ChannelPairResult { resInitPair :: ArbChannelPair , resSendChan :: ClientPayChan , resRecvChan :: ServerPayChan , resSentAmounts :: [BtcAmount] , resRecvdAmounts :: [BtcAmount] , resPayList :: [SignedPayment] } deriving (Generic, NFData) instance Show ArbChannelPair where show (ArbChannelPair spc rpc _ _ _ _) = "SendState: " ++ show spc ++ "\n" ++ "RecvState: " ++ show rpc instance Arbitrary ArbChannelPair where arbitrary = fmap fst mkChanPair instance Arbitrary ClientPayChan where arbitrary = fmap (sendChan . fst) mkChanPair instance Arbitrary ServerPayChan where arbitrary = fmap (recvChan . fst) mkChanPair instance Arbitrary (PayChanState BtcSig) where arbitrary = fmap rpcState (arbitrary :: Gen ServerPayChan) instance Arbitrary ChanParams where arbitrary = fmap fst mkChanParams instance Arbitrary FundingTxInfo where arbitrary = do ArbitraryTxHash h <- arbitrary i <- arbitrary amt <- choose (fromIntegral mIN_CHANNEL_SIZE, maxCoins) let fundAmount = either (error "Dust math fail") id $ mkNonDusty (fromIntegral amt :: BtcAmount) return $ CFundingTxInfo h i fundAmount instance Arbitrary BtcAmount where arbitrary = fromIntegral <$> choose (0, maxCoins) newtype NonZeroBitcoinAmount = NonZeroBitcoinAmount { getAmount :: BtcAmount } instance Arbitrary NonZeroBitcoinAmount where arbitrary = (NonZeroBitcoinAmount . fromIntegral) <$> choose (1, maxCoins) instance Arbitrary (Payment BtcSig) where arbitrary = snd <$> mkChanPair instance MonadTime Gen where currentTime = return nowishTimestamp toInitResult :: ArbChannelPair -> ChannelPairResult toInitResult initPair@(ArbChannelPair spc rpc payAmt rcvAmt pay _) = ChannelPairResult initPair spc rpc [payAmt] [rcvAmt] [pay] -- |Fold a payment of specified value into a 'ChannelPairResult' doPayment :: MonadTime m => ChannelPairResult -> BtcAmount -> m ChannelPairResult doPayment (ChannelPairResult initPair spc rpc sendList recvList payLst) amount = do (newSpc, pmn, amountSent) <- cappedCreatePayment spc amount eitherRpc <- acceptPayment rpc pmn case eitherRpc of Left e -> error (show e) Right (recvAmount, newRpc) -> return $ ChannelPairResult initPair newSpc newRpc (amountSent : sendList) (recvAmount : recvList) (pmn : payLst) runChanPair :: MonadTime m => ArbChannelPair -> [BtcAmount] -> m ChannelPairResult runChanPair chanPair paymentAmountList = foldM doPayment (toInitResult chanPair) paymentAmountList mkChanParams :: Gen (ChanParams, (HC.PrvKeyC, HC.PrvKeyC)) mkChanParams = do -- sender key pair ArbitraryPubKeyC sendPriv sendPK <- arbitrary -- receiver key pair ArbitraryPubKeyC recvPriv recvPK <- arbitrary -- ArbitraryXPubKey recvPriv recvPK <- arbitrary -- TODO: Use a future expiration date for now lockTime <- fromMaybe (error "Bad lockTime") . parseLockTime <$> choose (1795556940, maxBound) return (MkChanParams (MkSendPubKey sendPK) (MkRecvPubKey recvPK) lockTime, (sendPriv, recvPriv)) mkChanPair :: Gen (ArbChannelPair, SignedPayment) mkChanPair = arbitrary >>= mkChanPairInitAmount mkChanPairInitAmount :: BtcAmount -> Gen (ArbChannelPair, SignedPayment) mkChanPairInitAmount initPayAmount = do (cp, (sendPriv, recvPriv)) <- mkChanParams fti <- arbitrary sendChanE <- channelWithInitialPayment sendPriv cp fti (getFundingAddress cp) initPayAmount let (sendChan,initPayment) = either (error . show) id sendChanE recvChanE <- channelFromInitialPayment cp fti initPayment case recvChanE of Left e -> error (show e) Right (initRecvAmount,recvChan) -> return (ArbChannelPair --TODO: Cap initial payment amount sendChan recvChan initRecvAmount initRecvAmount initPayment recvPriv , initPayment) -- TODO: We don't bother testing expiration time for now nowishTimestamp :: UTCTime nowishTimestamp = UTCTime (ModifiedJulianDay 50000) 0