|
|
|
@ -20,6 +20,7 @@
|
|
|
|
|
{-# LANGUAGE TypeSynonymInstances #-}
|
|
|
|
|
|
|
|
|
|
import C.Zcash (rustWrapperUADecode)
|
|
|
|
|
import Control.Exception (throwIO)
|
|
|
|
|
import Control.Monad.IO.Class (liftIO)
|
|
|
|
|
import Data.Aeson
|
|
|
|
|
import Data.Bool (Bool(True))
|
|
|
|
@ -61,6 +62,7 @@ import ZcashHaskell.Types
|
|
|
|
|
, Phrase(..)
|
|
|
|
|
, RawData(..)
|
|
|
|
|
, RawTxResponse(..)
|
|
|
|
|
, Seed(..)
|
|
|
|
|
, ShieldedOutput(..)
|
|
|
|
|
, UnifiedAddress(..)
|
|
|
|
|
, UnifiedFullViewingKey(..)
|
|
|
|
@ -466,12 +468,18 @@ main = do
|
|
|
|
|
msg `shouldBe`
|
|
|
|
|
"Hello World!\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
|
|
|
|
|
describe "Wallet seed phrase" $ do
|
|
|
|
|
prop "Generated phrases are valid" prop_PhraseLength
|
|
|
|
|
prop "Derived seeds are valid" prop_SeedLength
|
|
|
|
|
prop "Orchard spending keys are valid" $
|
|
|
|
|
forAll genOrcArgs $ \(c, i, _) -> prop_OrchardSpendingKey c i
|
|
|
|
|
prop "Orchard receivers are valid" $
|
|
|
|
|
forAll genOrcArgs $ \(c, i, j) -> prop_OrchardReceiver c i j
|
|
|
|
|
prop "Generated phrases are valid" $ again prop_PhraseLength
|
|
|
|
|
prop "Derived seeds are valid" $ again prop_SeedLength
|
|
|
|
|
before getSeed $
|
|
|
|
|
describe "Optimized spending key tests" $ do
|
|
|
|
|
it "Sapling spending keys are valid" $ \s ->
|
|
|
|
|
property $ prop_SaplingSpendingKey s
|
|
|
|
|
it "Sapling receivers are valid" $ \s ->
|
|
|
|
|
property $ prop_SaplingReceiver s
|
|
|
|
|
it "Orchard spending keys are valid" $ \s ->
|
|
|
|
|
property $ prop_OrchardSpendingKey s
|
|
|
|
|
it "Orchard receivers are valid" $ \s ->
|
|
|
|
|
property $ prop_OrchardReceiver s
|
|
|
|
|
describe "Address tests" $ do
|
|
|
|
|
it "Encode transparent" $ do
|
|
|
|
|
let ua =
|
|
|
|
@ -481,7 +489,7 @@ main = do
|
|
|
|
|
Nothing -> "Bad UA"
|
|
|
|
|
Just u -> maybe "No transparent" encodeTransparent $ t_rec u
|
|
|
|
|
msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"
|
|
|
|
|
describe "Transparent Private and Publicc Key Generation" $ do
|
|
|
|
|
describe "Transparent Private and Public Key Generation" $ do
|
|
|
|
|
it "Obtain a transparent extended private key from HDSeed" $ do
|
|
|
|
|
let hdseed =
|
|
|
|
|
[ 206
|
|
|
|
@ -577,55 +585,38 @@ main = do
|
|
|
|
|
xtpubk <- xtpubkIO
|
|
|
|
|
---print $ show xtpubk
|
|
|
|
|
xtpubk `shouldBe` testpbk
|
|
|
|
|
describe "Sapling SpendingKey test" $ do
|
|
|
|
|
it "Generate Sapling spending key" $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
genSaplingSpendingKey <$> s `shouldNotBe` Nothing
|
|
|
|
|
describe "Sapling Payment Address generation test" $ do
|
|
|
|
|
it "Call genSaplingPaymentAddress" $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing
|
|
|
|
|
prop "Sapling receivers are valid" $
|
|
|
|
|
forAll genSapArgs $ \i -> prop_SaplingReceiver i
|
|
|
|
|
|
|
|
|
|
-- | Properties
|
|
|
|
|
prop_PhraseLength :: Int -> Property
|
|
|
|
|
prop_PhraseLength i =
|
|
|
|
|
prop_PhraseLength :: Property
|
|
|
|
|
prop_PhraseLength =
|
|
|
|
|
ioProperty $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
return $ BS.length p >= 95
|
|
|
|
|
|
|
|
|
|
prop_SeedLength :: Int -> Property
|
|
|
|
|
prop_SeedLength i =
|
|
|
|
|
prop_SeedLength :: Property
|
|
|
|
|
prop_SeedLength =
|
|
|
|
|
ioProperty $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
return $ maybe 0 BS.length s === 64
|
|
|
|
|
|
|
|
|
|
prop_OrchardSpendingKey :: CoinType -> Int -> Property
|
|
|
|
|
prop_OrchardSpendingKey c i =
|
|
|
|
|
ioProperty $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
return $ genOrchardSpendingKey (fromMaybe "" s) c i =/= Nothing
|
|
|
|
|
prop_OrchardSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
|
|
|
|
|
prop_OrchardSpendingKey s c (NonNegative i) =
|
|
|
|
|
genOrchardSpendingKey s c i =/= Nothing
|
|
|
|
|
|
|
|
|
|
prop_OrchardReceiver :: CoinType -> Int -> Int -> Property
|
|
|
|
|
prop_OrchardReceiver c i j =
|
|
|
|
|
ioProperty $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
let sk = genOrchardSpendingKey (fromMaybe "" s) c i
|
|
|
|
|
return $ genOrchardReceiver j (fromMaybe "" sk) =/= Nothing
|
|
|
|
|
prop_OrchardReceiver ::
|
|
|
|
|
Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property
|
|
|
|
|
prop_OrchardReceiver s c (NonNegative i) (NonNegative j) =
|
|
|
|
|
genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= Nothing
|
|
|
|
|
|
|
|
|
|
prop_SaplingReceiver :: Int -> Property
|
|
|
|
|
prop_SaplingReceiver i =
|
|
|
|
|
ioProperty $ do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
let sk = genSaplingSpendingKey (fromMaybe "" s)
|
|
|
|
|
return $ genSaplingPaymentAddress (fromMaybe "" sk) i =/= Nothing
|
|
|
|
|
prop_SaplingSpendingKey :: Seed -> NonNegative Int -> Property
|
|
|
|
|
prop_SaplingSpendingKey s (NonNegative i) =
|
|
|
|
|
genSaplingSpendingKey s i =/= Nothing
|
|
|
|
|
|
|
|
|
|
prop_SaplingReceiver :: Seed -> NonNegative Int -> NonNegative Int -> Property
|
|
|
|
|
prop_SaplingReceiver s (NonNegative i) (NonNegative j) =
|
|
|
|
|
genSaplingPaymentAddress (fromMaybe "" $ genSaplingSpendingKey s j) i =/=
|
|
|
|
|
Nothing
|
|
|
|
|
|
|
|
|
|
-- | Generators
|
|
|
|
|
genOrcArgs :: Gen (CoinType, Int, Int)
|
|
|
|
@ -637,4 +628,15 @@ genOrcArgs = do
|
|
|
|
|
|
|
|
|
|
genSapArgs :: Gen Int
|
|
|
|
|
genSapArgs = choose (1, 50)
|
|
|
|
|
|
|
|
|
|
getSeed :: IO Seed
|
|
|
|
|
getSeed = do
|
|
|
|
|
p <- generateWalletSeedPhrase
|
|
|
|
|
let s = getWalletSeed p
|
|
|
|
|
case s of
|
|
|
|
|
Nothing -> throwIO $ userError "Couldn't generate seed"
|
|
|
|
|
Just s' -> return s'
|
|
|
|
|
|
|
|
|
|
-- | Arbitrary instances
|
|
|
|
|
instance Arbitrary CoinType where
|
|
|
|
|
arbitrary = elements [MainNetCoin, TestNetCoin, RegTestNetCoin]
|
|
|
|
|