{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Bitcoin.PaymentChannel
import Data.Bitcoin.PaymentChannel.Types
import Data.Bitcoin.PaymentChannel.Util
import qualified Data.Bitcoin.PaymentChannel.Internal.State as S
import qualified Network.Haskoin.Transaction as HT
import qualified Network.Haskoin.Crypto as HC
import Network.Haskoin.Test
import Test.QuickCheck
dUST_LIMIT = 700 :: BitcoinAmount
mIN_CHANNEL_SIZE = 1400 :: BitcoinAmount
testAddrTestnet = "2N414xMNQaiaHCT5D7JamPz7hJEc9RG7469" :: HC.Address
testAddrLivenet = "14wjVnwHwMAXDr6h5Fw38shCWUB6RSEa63" :: HC.Address
data ArbChannelPair = ArbChannelPair
SenderPaymentChannel ReceiverPaymentChannel [BitcoinAmount] [BitcoinAmount] (HC.Hash256 -> HC.Signature)
instance Show ArbChannelPair where
show (ArbChannelPair spc rpc _ _ _) =
"SendState: " ++ show spc ++ "\n" ++
"RecvState: " ++ show rpc
doPayment :: ArbChannelPair -> BitcoinAmount -> ArbChannelPair
doPayment (ArbChannelPair spc rpc sendList recvList f) amount =
let
(amountSent, pmn, newSpc) = sendPayment spc amount
eitherRpc = recvPayment rpc pmn
in
case eitherRpc of
Left e -> error (show e)
Right (recvAmount, newRpc) ->
ArbChannelPair newSpc newRpc
(amountSent : sendList)
(recvAmount : recvList)
f
instance Arbitrary ArbChannelPair where
arbitrary = mkChanPair
mkChanPair :: Gen ArbChannelPair -- (Either String ArbChannelPair)
mkChanPair = do
-- sender key pair
ArbitraryPubKey sendPriv sendPK <- arbitrary
-- receiver key pair
ArbitraryPubKey recvPriv recvPK <- arbitrary
-- expiration date
lockTime <- arbitrary
fti <- arbitrary
let cp = CChannelParameters
(MkSendPubKey sendPK) (MkRecvPubKey recvPK) lockTime dUST_LIMIT
-- value of first payment
initPayAmount <- arbitrary -- fromIntegral <$> choose (0, chanAmount)
let (initPayActualAmount,paymnt,sendChan) = channelWithInitialPaymentOf
cp fti (flip HC.signMsg sendPriv) (HC.pubKeyAddr sendPK) initPayAmount
let eitherRecvChan = channelFromInitialPayment
cp fti (HC.pubKeyAddr sendPK) paymnt
case eitherRecvChan of
Left e -> error (show e)
Right (initRecvAmount,recvChan) -> return $ ArbChannelPair
sendChan recvChan [initPayActualAmount] [initRecvAmount]
(flip HC.signMsg recvPriv)
instance Arbitrary BitcoinLockTime where
arbitrary = fmap parseBitcoinLocktime arbitrary
instance Arbitrary FundingTxInfo where
arbitrary = do
ArbitraryTxHash h <- arbitrary
i <- arbitrary
amt <- fmap fromIntegral
(choose (fromIntegral mIN_CHANNEL_SIZE, round $ 21e6 * 1e8 :: Integer))
:: Gen BitcoinAmount
return $ CFundingTxInfo h i amt
instance Arbitrary BitcoinAmount where
arbitrary = fromIntegral <$> choose (0, round $ 21e6 * 1e8 :: Integer)
runChanPair :: ArbChannelPair -> [BitcoinAmount] -> (ArbChannelPair, [BitcoinAmount])
runChanPair chanPair paymentAmountList =
(foldl doPayment chanPair paymentAmountList, paymentAmountList)
checkChanPair :: (ArbChannelPair, [BitcoinAmount]) -> Bool
checkChanPair (ArbChannelPair sendChan recvChan amountSent amountRecvd recvSignFunc, _) = do
let settleTx = getSettlementBitcoinTx recvChan recvSignFunc testAddrLivenet 0
let clientChangeAmount = HT.outValue . head . HT.txOut $ settleTx
-- Check that the client change amount in the settlement transaction equals the
-- channel funding amount minus the sum of all payment amounts.
let fundAmountMinusPaySum = S.pcsChannelTotalValue (getChannelState recvChan) -
fromIntegral (sum amountSent)
let clientValueIsGood = fromIntegral clientChangeAmount == fundAmountMinusPaySum :: Bool
-- Debug print
let checkGoodClientValue goodCV = if not goodCV then
error $ "Bad client change value. Actual: " ++ show clientChangeAmount ++
" should be " ++ show fundAmountMinusPaySum
else
goodCV
-- Check that sender/receiver (client/server) agree on channel state
let statesMatch = getChannelState recvChan == getChannelState sendChan
-- Send/recv value lists match
let recvSendAmountMatch = amountSent == amountRecvd
checkGoodClientValue clientValueIsGood && statesMatch && recvSendAmountMatch
testChanPair :: ArbChannelPair -> [BitcoinAmount] -> Bool
testChanPair arbChanPair paymentAmountList =
checkChanPair (runChanPair arbChanPair paymentAmountList)
main :: IO ()
main = quickCheckWith stdArgs testChanPair