script

Primitive (Bitcoin) Script support for Haskell.
git clone git://git.ppad.tech/script.git
Log | Files | Refs | LICENSE

commit 376bc8ed61f64550c6b1057e7398e22b89284e7c
parent c3b4d4d041bfaaa66ba433f4f1311aeb21bd63f3
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 19 Jan 2025 15:14:06 +0400

test: more property test work

Diffstat:
Mtest/Main.hs | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 66 insertions(+), 9 deletions(-)

diff --git a/test/Main.hs b/test/Main.hs @@ -10,6 +10,7 @@ import Bitcoin.Prim.Script import qualified Crypto.Hash.SHA256 as SHA256 import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 import qualified Data.ByteString as BS +import qualified Data.ByteString.Base16 as B16 import qualified Data.Primitive.ByteArray as BA import Data.Word (Word8) import Test.Tasty @@ -32,6 +33,14 @@ instance Q.Arbitrary BS where b <- bytes 20_000 pure (BS b) +newtype HexBS = HexBS BS.ByteString + deriving (Eq, Show) + +instance Q.Arbitrary HexBS where + arbitrary = do + b <- bytes 20_000 + pure (HexBS (B16.encode b)) + instance Q.Arbitrary BA.ByteArray where arbitrary = do b <- bytes 20_000 @@ -44,7 +53,9 @@ newtype RawScript = RawScript Script instance Q.Arbitrary RawScript where arbitrary = fmap (RawScript . Script) Q.arbitrary --- will contain no or carefully-inserted pushdata opcodes +-- XX better generators for valid and invalid redeemscripts would be nice. +-- pushdata generation needs to be handled carefully. + newtype ValidRedeemScript = ValidRedeemScript Script deriving (Eq, Show) @@ -55,12 +66,25 @@ instance Q.Arbitrary ValidRedeemScript where bs <- fmap BS.pack (Q.vectorOf l (Q.chooseEnum (100, 255))) pure (ValidRedeemScript (Script (bs_to_ba bs))) +-- too large +newtype InvalidRedeemScript = InvalidRedeemScript Script + deriving (Eq, Show) + +instance Q.Arbitrary InvalidRedeemScript where + arbitrary = do + l <- Q.chooseInt (_MAX_REDEEM_SCRIPT_SIZE + 1, 20_000) + -- pushdata must be added with care; easy to blow up quickcheck + bs <- fmap BS.pack (Q.vectorOf l (Q.chooseEnum (100, 255))) + pure (InvalidRedeemScript (Script (bs_to_ba bs))) + -- properties ----------------------------------------------------------------- ba_to_bs_inverts_bs_to_ba :: BS -> Bool ba_to_bs_inverts_bs_to_ba (BS bs) = ba_to_bs (bs_to_ba bs) == bs --- to_base16 should work on every script, pathological or no +bs_to_ba_inverts_ba_to_bs :: BA.ByteArray -> Bool +bs_to_ba_inverts_ba_to_bs ba = bs_to_ba (ba_to_bs ba) == ba + from_base16_inverts_to_base16 :: RawScript -> Bool from_base16_inverts_to_base16 (RawScript s) = let mscript = from_base16 (to_base16 s) @@ -68,13 +92,34 @@ from_base16_inverts_to_base16 (RawScript s) = Nothing -> False Just script -> script == s +to_base16_inverts_from_base16 :: HexBS -> Bool +to_base16_inverts_from_base16 (HexBS bs) = + let mscript = from_base16 bs + in case mscript of + Nothing -> False + Just script -> to_base16 script == bs + -- we can only use 'from_script' on non-pathological scripts +-- +-- note the converse is not true to_script_inverts_from_script :: ValidRedeemScript -> Bool to_script_inverts_from_script (ValidRedeemScript s) = let !terms = from_script s !script = to_script terms in script == s +valid_redeem_script_produces_hash :: ValidRedeemScript -> Bool +valid_redeem_script_produces_hash (ValidRedeemScript s) = + case to_scripthash s of + Just {} -> True + _ -> False + +invalid_redeem_script_doesnt_produce_hash :: InvalidRedeemScript -> Bool +invalid_redeem_script_doesnt_produce_hash (InvalidRedeemScript s) = + case to_scripthash s of + Nothing -> True + _ -> False + -- assertions ----------------------------------------------------------------- base16_encoded_script_decodes_as_expected :: H.Assertion @@ -108,13 +153,25 @@ main :: IO () main = defaultMain $ testGroup "ppad-script" [ testGroup "property tests" [ - Q.testProperty "ba_to_bs . bs_to_ba ~ id" $ - Q.withMaxSuccess 500 ba_to_bs_inverts_bs_to_ba - , Q.testProperty "from_base16 . to_base16 ~ id" $ - Q.withMaxSuccess 500 from_base16_inverts_to_base16 - , Q.testProperty "to_script . from_script ~ id" $ - Q.withMaxSuccess 1000 to_script_inverts_from_script - ] + testGroup "inverses" [ + Q.testProperty "ba_to_bs . bs_to_ba ~ id" $ + Q.withMaxSuccess 500 ba_to_bs_inverts_bs_to_ba + , Q.testProperty "ba_to_bs . bs_to_ba ~ id" $ + Q.withMaxSuccess 500 bs_to_ba_inverts_ba_to_bs + , Q.testProperty "from_base16 . to_base16 ~ id" $ + Q.withMaxSuccess 500 from_base16_inverts_to_base16 + , Q.testProperty "to_base16 . from_base16 ~ id" $ + Q.withMaxSuccess 500 to_base16_inverts_from_base16 + , Q.testProperty "to_script . from_script ~ id" $ + Q.withMaxSuccess 1000 to_script_inverts_from_script + ] + , testGroup "hashes" [ + Q.testProperty "valid redeem script produces scripthash" $ + Q.withMaxSuccess 100 valid_redeem_script_produces_hash + , Q.testProperty "invalid redeem script doesn't produce scripthash" $ + Q.withMaxSuccess 100 invalid_redeem_script_doesnt_produce_hash + ] + ] , testGroup "unit tests" [ H.testCase "base16-encoded script decodes to expected terms" base16_encoded_script_decodes_as_expected