packages feed

haskoin-0.1.0: tests/Network/Haskoin/Script/Arbitrary.hs

{-|
  This module provides arbitrary instances for data types in
  'Network.Haskoin.Script'.
-}
module Network.Haskoin.Script.Arbitrary (ScriptOpInt(..))
where

import Test.QuickCheck 
    ( Gen
    , Arbitrary
    , arbitrary
    , oneof
    , choose
    , vectorOf
    , elements
    )
import Network.Haskoin.Crypto.Arbitrary()

import Control.Monad (liftM2)
import Control.Applicative ((<$>),(<*>))

import Data.Bits (testBit, clearBit, (.&.))
import Data.Word (Word8)

import Network.Haskoin.Util.Arbitrary (nonEmptyBS)
import Network.Haskoin.Script
import Network.Haskoin.Crypto

instance Arbitrary Script where
    arbitrary = do
        i <- choose (1,10)
        Script <$> (vectorOf i arbitrary)

instance Arbitrary ScriptOp where
    arbitrary = oneof [ opPushData <$> nonEmptyBS
                      , return $ OP_1NEGATE
                      , return $ OP_0
                      , return $ OP_1
                      , return $ OP_2
                      , return $ OP_3
                      , return $ OP_4
                      , return $ OP_5
                      , return $ OP_6
                      , return $ OP_7
                      , return $ OP_8
                      , return $ OP_9
                      , return $ OP_10
                      , return $ OP_11
                      , return $ OP_12
                      , return $ OP_13
                      , return $ OP_14
                      , return $ OP_15
                      , return $ OP_16

                      -- Flow control
                      , return $ OP_NOP
                      , return $ OP_IF
                      , return $ OP_NOTIF
                      , return $ OP_ELSE
                      , return $ OP_ENDIF
                      , return $ OP_VERIFY
                      , return $ OP_RETURN

                      -- Stack
                      , return $ OP_TOALTSTACK
                      , return $ OP_FROMALTSTACK
                      , return $ OP_2DROP
                      , return $ OP_2DUP
                      , return $ OP_3DUP
                      , return $ OP_2OVER
                      , return $ OP_2ROT
                      , return $ OP_2SWAP
                      , return $ OP_IFDUP
                      , return $ OP_DEPTH
                      , return $ OP_DROP
                      , return $ OP_DUP
                      , return $ OP_NIP
                      , return $ OP_OVER
                      , return $ OP_PICK
                      , return $ OP_ROLL
                      , return $ OP_ROT
                      , return $ OP_SWAP
                      , return $ OP_TUCK

                      -- Splice
                      , return $ OP_CAT
                      , return $ OP_SUBSTR
                      , return $ OP_LEFT
                      , return $ OP_RIGHT
                      , return $ OP_SIZE

                      -- Bitwise logic
                      , return $ OP_INVERT
                      , return $ OP_AND
                      , return $ OP_OR
                      , return $ OP_XOR
                      , return $ OP_EQUAL
                      , return $ OP_EQUALVERIFY

                      -- Arithmetic
                      , return $ OP_1ADD
                      , return $ OP_1SUB
                      , return $ OP_2MUL
                      , return $ OP_2DIV
                      , return $ OP_NEGATE
                      , return $ OP_ABS
                      , return $ OP_NOT
                      , return $ OP_0NOTEQUAL
                      , return $ OP_ADD
                      , return $ OP_SUB
                      , return $ OP_MUL
                      , return $ OP_DIV
                      , return $ OP_MOD
                      , return $ OP_LSHIFT
                      , return $ OP_RSHIFT
                      , return $ OP_BOOLAND
                      , return $ OP_BOOLOR
                      , return $ OP_NUMEQUAL
                      , return $ OP_NUMEQUALVERIFY
                      , return $ OP_NUMNOTEQUAL
                      , return $ OP_LESSTHAN
                      , return $ OP_GREATERTHAN
                      , return $ OP_LESSTHANOREQUAL
                      , return $ OP_GREATERTHANOREQUAL
                      , return $ OP_MIN
                      , return $ OP_MAX
                      , return $ OP_WITHIN

                      -- Crypto
                      , return $ OP_RIPEMD160
                      , return $ OP_SHA1
                      , return $ OP_SHA256
                      , return $ OP_HASH160
                      , return $ OP_HASH256
                      , return $ OP_CODESEPARATOR
                      , return $ OP_CHECKSIG
                      , return $ OP_CHECKSIGVERIFY
                      , return $ OP_CHECKMULTISIG
                      , return $ OP_CHECKMULTISIGVERIFY

                      -- More NOPs
                      , return $ OP_NOP1
                      , return $ OP_NOP2
                      , return $ OP_NOP3
                      , return $ OP_NOP4
                      , return $ OP_NOP5
                      , return $ OP_NOP6
                      , return $ OP_NOP7
                      , return $ OP_NOP8
                      , return $ OP_NOP9
                      , return $ OP_NOP10

                      , return $ OP_INVALIDOPCODE 0xff
                      ]

instance Arbitrary PushDataType where
    arbitrary = elements [ OPCODE, OPDATA1, OPDATA2, OPDATA4 ]

instance Arbitrary TxSignature where
    arbitrary = liftM2 TxSignature arbitrary arbitrary

instance Arbitrary SigHash where
    arbitrary = do
        oneof [ SigAll    <$> arbitrary
              , SigNone   <$> arbitrary
              , SigSingle <$> arbitrary
              , f
              ]
      where
        f = do
            wGen <- arbitrary :: Gen Word8
            -- Make sure we don't have an unknown which is actually known
            let w = if clearBit wGen 7 <= 3 then wGen .&. 0xfc else wGen
            return $ SigUnknown (testBit w 7) w

instance Arbitrary ScriptOutput where
    arbitrary = oneof [ genSimpleOutput
                      , genPaySHOutput
                      ]

genSimpleOutput :: Gen ScriptOutput
genSimpleOutput = oneof
    [ PayPK <$> arbitrary
    , (PayPKHash . pubKeyAddr) <$> arbitrary 
    , genPayMulSig =<< choose (1,16)
    ]
    
genPaySHOutput :: Gen ScriptOutput
genPaySHOutput = (PayScriptHash . scriptAddr) <$> genSimpleOutput

-- | Generate an arbitrary 'ScriptOutput' of value PayMulSig.
genPayMulSig :: Int -> Gen ScriptOutput
genPayMulSig m = do
    n <- choose (m,16)
    PayMulSig <$> (vectorOf n arbitrary) <*> (return m)

instance Arbitrary SimpleInput where
    arbitrary = oneof
        [ SpendPK <$> arbitrary
        , SpendPKHash <$> arbitrary <*> arbitrary
        , genSpendMulSig =<< choose (1,16)
        ]

instance Arbitrary ScriptInput where
    arbitrary = oneof
        [ RegularInput <$> arbitrary
        , genScriptHashInput
        ]

-- | Generate an arbitrary 'SimpleInput of value SpendMulSig.
genSpendMulSig :: Int -> Gen SimpleInput
genSpendMulSig r = do
    s <- choose (1,r)
    SpendMulSig <$> (vectorOf s arbitrary)

genScriptHashInput :: Gen ScriptInput
genScriptHashInput = do
    inp <- arbitrary :: Gen SimpleInput
    out <- case inp of
        SpendPK _        -> PayPK <$> arbitrary
        SpendPKHash _ _  -> (PayPKHash . pubKeyAddr) <$> arbitrary
        SpendMulSig sigs -> genPayMulSig $ length sigs
    return $ ScriptHashInput inp out

-- | Data type for generating an arbitrary 'ScriptOp' with a value in
-- [OP_1 .. OP_16]
data ScriptOpInt = ScriptOpInt ScriptOp
    deriving (Eq, Show)

instance Arbitrary ScriptOpInt where
    arbitrary = ScriptOpInt <$> elements 
                    [ OP_1,  OP_2,  OP_3,  OP_4
                    , OP_5,  OP_6,  OP_7,  OP_8
                    , OP_9,  OP_10, OP_11, OP_12
                    , OP_13, OP_14, OP_15, OP_16
                    ]