zcash-haskell/test/Spec.hs
Rene Vergara d45bd7dcf3
Milestone 3 (#101)
This PR contains all the enhancements needed for the completion of Milestone 3 of the Zenith Full Node wallet.
- Implementation of Rust Sapling parameters
- Implementation of native Haskell commitment trees
- Optimization of transaction creation

Reviewed-on: https://git.vergara.tech///Vergara_Tech/zcash-haskell/pulls/101
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-11-21 14:19:43 +00:00

1336 lines
146 KiB
Haskell

{- Copyright 2022-2024 Vergara Technologies LLC
This file is part of Zcash-Haskell.
Zcash-Haskell is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
Zcash-Haskell. If not, see <https://www.gnu.org/licenses/>.
-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
import C.Zcash (rustWrapperUADecode)
import Control.Exception (throwIO)
import Control.Monad.IO.Class (liftIO)
import Data.Aeson
import Data.Bool (Bool(True))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import Data.Either (isRight)
import Data.Foldable (sequenceA_)
import Data.HexString
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified Data.Text.Lazy.Encoding as LE
import qualified Data.Text.Lazy.IO as LTIO
import Data.Word
import Foreign.C.Types
import GHC.Float.RealFracMethods (properFractionDoubleInteger)
import Haskoin.Crypto.Hash (ripemd160)
import Haskoin.Crypto.Keys.Extended
import Haskoin.Transaction.Common
import Network.HTTP.Simple (Response(..))
import Test.HUnit
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
import ZcashHaskell.Orchard
import ZcashHaskell.Sapling
( decodeSaplingAddress
, decodeSaplingOutput
, decodeSaplingOutputEsk
, encodeSaplingAddress
, genSaplingInternalAddress
, genSaplingPaymentAddress
, genSaplingSpendingKey
, getSaplingFrontier
, getSaplingNotePosition
, getSaplingWitness
, getShieldedOutputs
, isValidSaplingViewingKey
, isValidShieldedAddress
, matchSaplingAddress
, updateSaplingCommitmentTree
, updateSaplingWitness
)
import ZcashHaskell.Transparent
import ZcashHaskell.Types
( AccountId
, BlockResponse(..)
, CoinType(..)
, DecodedNote(..)
, OrchardAction(..)
, OrchardBundle(..)
, OrchardCommitmentTree(..)
, OrchardFrontier(..)
, OrchardSpendingKey(..)
, OrchardWitness(..)
, Phrase(..)
, RawData(..)
, RawOutPoint(..)
, RawTBundle(..)
, RawTxIn(..)
, RawTxOut(..)
, RawTxResponse(..)
, RawZebraTx(..)
, RpcError(..)
, RpcResponse(..)
, SaplingAddress(..)
, SaplingBundle(..)
, SaplingCommitmentTree(..)
, SaplingFrontier(..)
, SaplingReceiver(..)
, SaplingSpendingKey(..)
, SaplingWitness(..)
, Scope(..)
, Seed(..)
, ShieldedOutput(..)
, ToBytes(..)
, TransparentAddress(..)
, TransparentBundle(..)
, TransparentReceiver(..)
, TransparentType(..)
, UnifiedAddress(..)
, UnifiedFullViewingKey(..)
, ZcashNet(..)
, ZebraTxResponse(..)
, decodeHexText
, fromRawOBundle
, fromRawSBundle
, fromRawTBundle
, getValue
)
import ZcashHaskell.Utils
m2bs :: Maybe BS.ByteString -> BS.ByteString
m2bs x = fromMaybe "" x
main :: IO ()
main = do
hspec $ do
describe "Bech32" $ do
let s = "abc14w46h2at4w46h2at4w46h2at4w46h2at958ngu"
let decodedString = decodeBech32 s
it "hrp matches" $ do hrp decodedString `shouldBe` "abc"
xit "data matches" $ do
bytes decodedString `shouldBe` BS.pack ([0x00, 0x01, 0x02] :: [Word8])
it "encoding works" $ do
encodeBech32m "abc" (bytes decodedString) `shouldBe`
E.decodeUtf8Lenient s
describe "F4Jumble" $ do
it "jumble a string" $ do
let input =
[ 0x5d
, 0x7a
, 0x8f
, 0x73
, 0x9a
, 0x2d
, 0x9e
, 0x94
, 0x5b
, 0x0c
, 0xe1
, 0x52
, 0xa8
, 0x04
, 0x9e
, 0x29
, 0x4c
, 0x4d
, 0x6e
, 0x66
, 0xb1
, 0x64
, 0x93
, 0x9d
, 0xaf
, 0xfa
, 0x2e
, 0xf6
, 0xee
, 0x69
, 0x21
, 0x48
, 0x1c
, 0xdd
, 0x86
, 0xb3
, 0xcc
, 0x43
, 0x18
, 0xd9
, 0x61
, 0x4f
, 0xc8
, 0x20
, 0x90
, 0x5d
, 0x04
, 0x2b
] :: [Word8]
let out =
[ 0x03
, 0x04
, 0xd0
, 0x29
, 0x14
, 0x1b
, 0x99
, 0x5d
, 0xa5
, 0x38
, 0x7c
, 0x12
, 0x59
, 0x70
, 0x67
, 0x35
, 0x04
, 0xd6
, 0xc7
, 0x64
, 0xd9
, 0x1e
, 0xa6
, 0xc0
, 0x82
, 0x12
, 0x37
, 0x70
, 0xc7
, 0x13
, 0x9c
, 0xcd
, 0x88
, 0xee
, 0x27
, 0x36
, 0x8c
, 0xd0
, 0xc0
, 0x92
, 0x1a
, 0x04
, 0x44
, 0xc8
, 0xe5
, 0x85
, 0x8d
, 0x22
] :: [Word8]
BS.pack out `shouldBe` f4Jumble (BS.pack input)
it "unjumble a string" $ do
let input =
[ 0x5d
, 0x7a
, 0x8f
, 0x73
, 0x9a
, 0x2d
, 0x9e
, 0x94
, 0x5b
, 0x0c
, 0xe1
, 0x52
, 0xa8
, 0x04
, 0x9e
, 0x29
, 0x4c
, 0x4d
, 0x6e
, 0x66
, 0xb1
, 0x64
, 0x93
, 0x9d
, 0xaf
, 0xfa
, 0x2e
, 0xf6
, 0xee
, 0x69
, 0x21
, 0x48
, 0x1c
, 0xdd
, 0x86
, 0xb3
, 0xcc
, 0x43
, 0x18
, 0xd9
, 0x61
, 0x4f
, 0xc8
, 0x20
, 0x90
, 0x5d
, 0x04
, 0x2b
] :: [Word8]
let out =
[ 0x03
, 0x04
, 0xd0
, 0x29
, 0x14
, 0x1b
, 0x99
, 0x5d
, 0xa5
, 0x38
, 0x7c
, 0x12
, 0x59
, 0x70
, 0x67
, 0x35
, 0x04
, 0xd6
, 0xc7
, 0x64
, 0xd9
, 0x1e
, 0xa6
, 0xc0
, 0x82
, 0x12
, 0x37
, 0x70
, 0xc7
, 0x13
, 0x9c
, 0xcd
, 0x88
, 0xee
, 0x27
, 0x36
, 0x8c
, 0xd0
, 0xc0
, 0x92
, 0x1a
, 0x04
, 0x44
, 0xc8
, 0xe5
, 0x85
, 0x8d
, 0x22
] :: [Word8]
f4UnJumble (BS.pack out) `shouldBe` BS.pack input
describe "JSON parsing" $ do
it "block response" $ do
j <- LTIO.readFile "block.json"
let p = eitherDecode $ LE.encodeUtf8 j :: Either String BlockResponse
case p of
Left s -> s `shouldBe` ""
Right x -> bl_height x `shouldBe` 2196277
it "raw transaction response" $ do
j <- LTIO.readFile "tx.json"
let t = eitherDecode $ LE.encodeUtf8 j :: Either String RawTxResponse
case t of
Left s -> s `shouldBe` ""
Right x ->
toText (rt_id x) `shouldBe`
"5242b51f22a7d6fe9dee237137271cde704d306a5fff6a862bffaebb6f0e7e56"
describe "Seeds" $ do
it "generate seed phrase" $ do
s <- generateWalletSeedPhrase
BS.length (getBytes s) `shouldNotBe` 0
it "get seed from phrase" $ do
s <- generateWalletSeedPhrase
let x = getWalletSeed s
let result =
case x of
Nothing -> False
Just s' -> True
result `shouldBe` True
describe "Sapling address" $ do
it "succeeds with valid address" $ do
let sa =
"zs17faa6l5ma55s55exq9rnr32tu0wl8nmqg7xp3e6tz0m5ajn2a6yxlc09t03mqdmvyphavvf3sl8"
isValidShieldedAddress sa `shouldBe` True
it "fails with invalid address" $ do
let sa =
"zs17faa6l5ma55s55exq9rnr32tu0wl8nmqg7xp3e6tz0m5ajn2a6yxlc09t03mqdmvyphavvffake"
isValidShieldedAddress sa `shouldBe` False
describe "Decode Sapling VK" $ do
let vk =
"zxviews1qdjagrrpqqqqpq8es75mlu6rref0qyrstchf8dxzeygtsejwfqu8ckhwl2qj5m8am7lmupxk3vkvdjm8pawjpmesjfapvsqw96pa46c2z0kk7letrxf7mkltwz54fwpxc7kc79mm5kce3rwn5ssl009zwsra2spppwgrx25s9k5hq65f69l4jz2tjmqgy0pl49qmtaj3nudk6wglwe2hpa327hydlchtyq9av6wjd6hu68e04ahwk9a9n2kt0kj3nj99nue65awtu5cwwcpjs"
let sa =
"zs1g2ne5w2r8kvalwzngsk3kfzppx3qcx5560pnfmw9rj5xfd3zfg9dkm7hyxnfyhc423fev5wuue4"
let sa' =
"zs17faa6l5ma55s55exq9rnr32tu0wl8nmqg7xp3e6tz0m5ajn2a6yxlc09t03mqdmvyphavvf3sl8"
let rawKey = decodeBech32 vk
let rawSa = decodeBech32 sa
let rawSa' = decodeBech32 sa'
it "is mainnet" $ do hrp rawKey `shouldBe` "zxviews"
it "is valid Sapling extended full viewing key" $ do
isValidSaplingViewingKey vk `shouldBe` True
it "matches the right Sapling address" $ do
matchSaplingAddress (bytes rawKey) (bytes rawSa) `shouldBe` True
it "doesn't match the wrong Sapling address" $ do
matchSaplingAddress (bytes rawKey) (bytes rawSa') `shouldBe` False
describe "Decode invalid Sapling VK" $ do
let vk =
"zxviews1qdjagrrpqqqqpq8es75mlu6rref0qyrstchf8dxzeygtsejwfqu8ckhwl2qj5m8am7lmupxk3vkvdjm8pawjpmesjfapvsqw96pa46c2z0kk7letrxf7mkltwz54fwpxc7kc79mm5kce3rwn5ssl009zwsra2spppwgrx25s9k5hq65f69l4jz2tjmqgy0pl49qmtaj3nudk6wglwe2hpa327hydlchtyq9av6wjd6hu68e04ahwk9a9n2kt0kj3nj99nue65awtu5cwwfake"
let rawKey = decodeBech32 vk
it "is not mainnet" $ do hrp rawKey `shouldBe` "fail"
describe "Unified address" $ do
it "succeeds with correct UA" $ do
let ua =
"u1salpdyefywvsg2dlmxg9589yznh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur"
isJust (isValidUnifiedAddress ua) `shouldBe` True
it "fails with incorrect UA" $ do
let ua =
"u1salpdyefbreakingtheaddressh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur"
isValidUnifiedAddress ua `shouldBe` Nothing
it "encodes UA correctly" $ do
let ua =
"u1salpdyefywvsg2dlmxg9589yznh0h9v6qjr478k80amtkqkws5pr408lxt2953dpprvu06mahxt99cv65fgsm7sw8hlchplfg5pl89ur"
(encodeUnifiedAddress <$> isValidUnifiedAddress ua) `shouldBe`
Just (E.decodeUtf8Lenient ua)
describe "Decode UVK from YWallet" $ do
let uvk =
"uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"
let res = decodeUfvk uvk
it "is mainnet" $ do maybe 0 net res `shouldBe` 1
it "has Orchard key" $ do BS.length (maybe "" o_key res) `shouldBe` 96
it "has Sapling key" $ do BS.length (maybe "" s_key res) `shouldBe` 128
it "does not have Transparent key" $ do
BS.length (maybe "" t_key res) `shouldBe` 1
describe "Decode bad UVK" $ do
it "should fail" $ do
let fakeUvk =
"uview1u83changinga987bundchofch4ract3r5x8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"
decodeUfvk fakeUvk `shouldBe` Nothing
describe "Check if UA and UVK match" $ do
let ua =
"u15hjz9v46azzmdept050heh8795qxzwy2pykg097lg69jpk4qzah90cj2q4amq0c07gta60x8qgw00qewcy3hg9kv9h6zjkh3jc66vr40u6uu2dxmqkqhypud95vm0gq7y5ga7c8psdqgthsrwvgd676a2pavpcd4euwwapgackxa3qhvga0wnl0k6vncskxlq94vqwjd7zepy3qd5jh"
let ua' =
"u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x"
let uvk =
"uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"
it "succeeds with correct address" $ do
matchOrchardAddress uvk ua `shouldBe` True
it "fails with wrong address" $ do
matchOrchardAddress uvk ua' `shouldBe` False
describe "Decode Sapling tx" $ do
let svk =
"zxviews1qvapd723qqqqpqq09ldgykvyusthmkky2w062esx5xg3nz4m29qxcvndyx6grrhrdepu4ns88sjr3u6mfp2hhwj5hfd6y24r0f64uwq65vjrmsh9mr568kenk33fcumag6djcjywkm5v295egjuk3qdd47atprs0j33nhaaqep3uqspzp5kg4mthugvug0sc3gc83atkrgmguw9g7gkvh82tugrntf66lnvyeh6ufh4j2xt0xr2r4zujtm3qvrmd3vvnulycuwqtetg2jk384"
let badvk =
"zxviews1qvapd723ffakeqq09ldgykvyusthmkky2w062esx5xg3nz4m29qxcvndyx6grrhrdepu4ns88sjr3u6mfp2hhwj5hfd6y24r0f64uwq65vjrmsh9mr568kenk33fcumag6djcjywkm5v295egjuk3qdd47atprs0j33nhaaqep3uqspzp5kg4mthugvug0sc3gc83atkrgmguw9g7gkvh82tugrntf66lnvyeh6ufh4j2xt0xr2r4zujtm3qvrmd3vvnulycuwqtetg2jk384"
let rawKey = decodeBech32 svk
let badKey = decodeBech32 badvk
let rawTx =
fromText
"050000800a27a726b4d0d6c200000000ff8e210000000001146cc65bd6d252d09b8eb0a8ab0aab6d7a798325aefc1d3032fc6d31373a85a25a3a16b447a698f720ade1bc290a74d85574b5b20515391035a67f8d5883dc65ea3ba4a17b009d6f325d41072b3ce240270959a7ffd040e5f16c697d8148973c62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d23301c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668f0d8ffffffffffff9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900c734b62e56792f6c8bed48e4f108a817e83d64d6a59e38cfdb55c0f8a89bc7507c89326266f7ac03a3941f448cb879bd792bb116d0be8876c0856a76ddec0f0c02e16f0338626013ee5f6037fc6a3c69fa291204039d04d17c11295ee3024aea8f5d381e9b7eb3f938b6f9182bf4f889f1e53e30f998b1cdd23f45cfaa20aaef058248cc2e1c487fcdf54a4bc22a68a17cb6fa7b2fbf333b99feb84643d321398b675634929602126b2fb40171e514769bf82f18c267ce9cda0c24300caa9a5a361144d3b7b9ab2243ee9811d9b2e72c8bb1d145cdfcf6b29994a969b41c47208f5dba8d6d871e490e9b970afec4d8bca40ba51825cdc78cc7cde6b6f235a4105b1d1b5e2765efd753095ce770f070b02cce3316721b9345680c146c2f428c0bbca90d5a8cd0a1c4c31cbfa8ec165ea9f9c71d2d05e3cf8bae5e779786f179c45a3cd8087d820cae812aded04f8acda9068af80ea834f79f1bd03bfd66f8a19074649a85ce877df1a621a867debb423ec0d19015b326fcf6f143aba34029c1da2fc7b099378a366c38c9609ef6a9d9e175e21b0c1ab94a84e28ee7f1a00e39cb6fb59f44e4567e9f85f8f98158263c52ec433c042397c784edb07c28d2bca036f59090e819157375d610acb1993a4107b48da13a371f5383429baee209b2c0cc150fcef79a042749668ba1f89ad24a8c746142191ed0e8fd63624a331d98d50daa84ccf9043076947cf5115b9f8787acd36000c5e72c8d783b29bb28a3e46036d0a592ce8a158ee5a7ac210be72d3a6185c13645d96a8446021b64043ab8b589a20091c152e7d5a993ba94770eea988e289e1536d0d81dbc7046ca9c6d918446bf970894f073c920006681ccf6d1a3f138519c68eba0296069e42dc60f2bcd0f17c400efe4f4e87de8606606dc4fdf31494df4d454d14a440b1d9db4265c7aa9bc8683c68cb149f2cc826427575e2af82e842199a9cb9fdc7243b3bc12f1a71c37eac5cf88ba830cb95728897fa4c177a290d6b2b3814173262da14db9b4ef39fc54f888a6ffef4221ae672fb03bc78ebef479360a682ddb12ea0369a428a6c2960ff8327e9a2f5e5d98ce1eae748db8f6a4631c789b4d751d6b99c97c149a813998d44a7b57ba06c8bcb8a6c73c6388cdcfeb1346cec8fee7bdebf2a2388d9722183eb2d2e0e183cdd092152ef640880f4514f3c5e836cc3a8249413500630aa8da85f9e3cd92bdadbb69a2bab8d71f0b3ec5832a7ddbddd67b34c33b2e12a0c8468e852e4a8f7df45657e9632088aa7c6c5048a2686019cfec33b27fc88e23759938dd55a5dff589c1c21a37da617609e9d8be37dbf9bd6e84ee160fe10268171d969e4611afe9d3482ed4b132dcdd11ee516f36d512a333da20266fd984caebf4937fdfd18ed07b4a45771cf5c8c16c6b258b289a07d136a22acc766011f366c420bafb8fc1a10e42219bede5a3d1166c525491ab60bbd1f973fd3fb2e94cea888e24d5fb0adce51faeda75d62de70094d4b36d38d03cd824d284fad577c3ead4d98bcc8ceccd18174a889b22380bfcc12656e764ea0b8fe1409971283008ed02cbef89d6f544c62c3b001bfe96723fda9190deecba534d69cfa358036fdaf16127b89f925c52d4e750919ffb7182b6a8ad13d0a8e00e0b906978dd24ee11869c1a63837a80e46e1216e2e273aba07aa5b0d97558db0ba7f9ac4c89403c65f1719394e479311f5cf84746e6be6f1abcac03194aa8bf1735811198b5df90dd6cac345779c185c24beda0101b932048dc4144af664a63acc0c395052882ee1f18bd0ddf13bb583861923bc00ed5ae815b964698ca097eda1c4281e039139fa3091890244f926cc4ab773ca8a35d5263d3bb48fd6ac53a4bb4d7d60b36446dbc714c35b5e13a17c5b0c70f67207839d1f7404604aff63b2fa83a4da7dac92aac96b3f250412f8d04a9e298004313b02edefd076c67d8a1316355777814e2e1ab03690e426b672d32ff65c03c592ecce6a70e34fea2e15b9a6b4fd092d027199caf27e84e25c09380b38a5eb8985355b3259aa1d94be74269b84f953053b02ba3be9df872ae5fb2d893188575bdfe222ba267b5461a0d0be274a7d9e6ee51490d98e4cd97978804c4f0f8e9f4908fd8c102b01080f5a02b7578591e95d60f3f56d8e48514b1ce7ea6894f55a32c8ac8564985d18c6b82f8dcde5b315624e9321bdd49dd350c87907cc373c0238a79321e6250e38a0ceb2c060ecee6708c11cb30a49687da9923bcdf011f9aca27e6eb5a8477a2bae2dcff9884cc2349b51a66b5179ed2d8f69e4bbba74c694194e83d04a8566228227eb732a95180c6788483d1f259d52c52fe43357656d50a1cf2902c3124d60d15fc85f0447a1203f824c1106452cfec1c92b18de003f82a0000000000001cbd27436a221a53d08c4838831d1bc60ff7e93df41a51412ef6096eec98bb28fd601c53a5371b23a497062635b5cdde715c23840d37f1cf328f0a2ba96260357689ae3f84a80dbdca1520df68513be1285177d3c0da664c64944de78d8b8d5864f5ac15444cd3204adc4fe487503066c18fbbef8d0515248b0a97577f5aea1d255788ed4bb66d4d56303efe135063392c312b4671963daa20e0ade262984e11263a1588eba3cf829e6131ab506e6a850aacce603e8ecfd6e794c90a772603d80fd2aad6027b34854072a0d23079252adb1ba637bbc650ed4afd35d977e1498d998020bc1c814718b48ba7378a92c56827d3c2f20daa231fa51f0a9188520e2a11149e162489f0d6dbd27cf94fd5775311d3dfbcfeb431bafc3515bbb8c4ba4488c320dca0dfec548fe9f46d8810b3f6b16bb3e3eb0ea130747d3d127c5953ca8d561f8d425a35dc3f2cd831743139fbdcada42308b524313782e23b32d5d54a265eae408623e3b2779fe60e13cf47d54dfe520f9f4e57c68aed31f78629a9074d72ab87bea993a38f95ab40df3ef01735e7d44ad365a786e0d3032f1c1dc4e6839c974185dbe63f8725e79831ebe269f94c96705639ab38d5d0700da04c6a9f686e1ea13391885287ba43cf3ccef1c2227918f15ed55441c45adca84153530bbfea3cf37adbf84831a2bfcbf0ca4a4bbd90e623789fe993dc17503ec11b1ef3049f27b27ff778af364d634a46165cda1dd8241cb88740bce74a73e7e3d656df2dee05bb561a85e64671b191ec802c5bfaca49b8168e44271cf13df756395896ff41a99654f55b6951f20d04b2007938a420218db8e37445ef3267130e288e3270b13a92596a26043e1ae84f3934cdb13363bc2843f74a0f6608a36b52c985132aa427c56b7275a864b3c76502c37b8abb8d0286b3199c78492ba8103f5a23c6cdca2292c75d7d6d7080108850807f78af3dc7e418371c6b8951bd89b79fa586af4e16096b08ac1f4dc2b1e4feaa5c040bb002b57311523197b6e2bef5b79ac9c9b4a339be6f6bf7fbe9b5c93862c87be6647949c70bb2c7e268e2ab39cbadd69de628376b3af744eeabc85b599bbdd09defacefa443e05c9b5f259a7783743fecf1a749c57cacc85703269ed67db1d8d475f6fe25d66f84a77379411ba123d98fcb3ae4eec306489a08372893616a91268ea6bf34ddbf0fdef1360ab9e82f4ac80a24e41f439af06fadc223c61f445b7261eda5e1320e269d1277631ee2245cf930244bf8c04050c514e2d59035b80827586cbfeb7da7a59c1208aa86390b9dc7a9b6ef38879ba4deea5eef47c5c98d9167594cd730abdfaf082090efe759d1b13199d739c112ae324ba24b275bf1d89867b81f4580a7ea3a8d3d07b45e2de6c1c7099de3606873b13f3083ecd1e84456c9a1b1d358075c68b1a7cf0b1f26031a2909e226f5da7877d0085b879165ec4b1d9abb7b0732ab4a6f22d9a7bbd0d494ef3f9af4903dc733fe92c6b2f557d1406d223a93e8ad6e579ebcde9c39a5652ad31335df924e5b6a09a0191821b4a0c8f886e2d7860b75ae79ad9dfbebf3500c8b9762dcd131eb5c8b866b5efb4fbfdcc5e31605c2b7d2ff8db5198a6c41bcf880065ff232ff8f84ca3f8022d3428359dc9fb19f57a6ad3f3d174d8a348879a754b37095f01d9a7f6f873798b97dfc5d7c7eaf0383b3fdccdcc11b30dbb3a0fe3186a36c4ddc9674624e38a81cca60a9bbc1b124021b61a383b7547d6af187022c133ba9d6dadf711a3af3b0255b859b214ef6c5dec592248fc94339a64f19196ca0fdad80f7f8e3d78b1f783b1f038008d0d106bd86e23e33ae5728872d42a555bb36d0e3303f0b4ab41180f4251590ee3ee244b77191c31b9f3f990f71c6e237b9dbcf7ca21c9b4c2446b856c67861785bb9edb920b8f530a7a088313ef044419a879f26db137e1557d079315844f9f60bae03d8cffa7a28bd2857a001fd5d2d999fca95ff91df0e228567f6c9ff592b77b7ccdc93a951f7e34910361a8f4fb517e1c9fb956a3bb50ddf37ed37e8d26adfc0f71e059ba95ec77a1e34e1b3420c893f89b79fc72e3a1d864fd35526727f939badc29740f5ef9c0bb9a3f72e1e08b2ef2ab366f80d8e14e03e92162736e2ccb7cde82b2af08de8a6a81c03c63e2396974ec29fb122d818a2d2d5b29d11b704d3ac3b39431099b7b2f6aae04d28a2182b55380503127e4986ee9e8d5c0c2058e09e4592d08d013a4f088e45403720160622236bd56fbd9cc240efceb1b23c19ceafec49e9d5776ca9da5f7810ac979cf6ec5f678c09257abf79c9ab55dea00054e11b62c0a0ed4363d0a96a37ae1a323aa93bb1af253885afb684ed30caf5cf3b37afe6a6463a16f34cc28b4530c6bc6281f597bea476cd9a773205b96d47ed4b0bfeeee39b7ea44ff194911bcddbb161c2c0ce9488978b99e880d8e43624dbb4a567483ed293348d752634b2f46219575175e3c249b8e4e853142b66491aa1c142e7bb558955747cf2bd61ac802a2a4784d9fe080f771dd537d0ff928b3b04029d9ac03175c2d535ad7f3c123eff30c0437b32dd9fa31b2976e369b89b79a2a95e31aee15a462c5fe25fd937ce6b0795808d16163f2cea8f19b7c83913cb4a793576aa5c0ddfb6415326d8f2be0621017616c85ee46aa768b077dbec72311cb8a0f78ba0621a277a79af6607063839a52c6825a20e1c5818b24628862aff5fbbfe87311866b9956eeea7412ef69a3e4da84699b8d8b45c74ac96c3356989fd4962ba79fc26a92488304fd9f42486266b433eeb57368d26ae91ba4e7ed812581f790314cc7f44639aa7f6775618111369e8e2d68a6ab24388824bbbe3c3b0fbfb88635c1fc3216af1af40eba3555c0b0390a18ccba9e68afbcd21ecb212aaf82846f0a55793945902c48cac5d19332e23248a464529f4cca177137c508b6b13637e7df523254f24b8343d19164174202bcb00d5fe618b760c374f69b5065b1f9acf91af95abd7eb271586cae14fc835f633aacb4cd2ecd0f0ac08b688ef4d13b8a7b4c487ad46485bde0e340309672dc38af275e6ab525971409a39eff0ad134b1674db5d1f9725874e36d8730dc034b0a596c6e0a26e521c199d3e3f86815a64d148ffc394290b19f15390934b5d0da27dc8365360511628b93ebc375d2a531e4f4cfa031eeb501afe96d201c7b6078bbceb8e5d8615599f4c613bbb81a88f4eeaa57a9c008125073a30154044c422eaf2a32cecb15aab0774bb44e52b1792d154b8036ff9224af53e023fc011ab251d47d7d76e55c5015db1926d43c56d055feca1259de53ce98a2faecb5843ce17a3e83ccf678f5d13a8321f278a670c684e62b720e1eefd0abb2e9d0a3ddea81d5eb6e380a1c22af5587daac852e93a86f5e4293c18bb26b32035c7e5ca20cd2e3eabd3de0e55092a79a42c7ccc0aef033d6683043c2a29531a2ef1f503595c0e464ac042153c4685b062275f88bab93cc017f1ef9dc6f8aeab7b0b234f1c543420324512554f1786c82b37836238a4dfdf86f97d09ae466eb39d5f3ab159e2060be309d1284be133ab40abd61468c1706f7f9e57a7bc747479693a03edc8863dd196fd7cb2721260e42f4f606389c4972c74d357e7467b61cb7f455562015d29a59c7cafe0df03f26b77bca81c2bbac8cbccf8a65190b0c4e5ca832e82ce4e11044433aa397106cafc05634ad778270d20d8a13068586bc6b582ea24524fd921a5ee22dbae5296ee86d80f12b78bba26f8b42c1401b75d5cfcc4775c5cd1cb0a9248dabc8f82d216787b2a2780f7d13a5ee8c6ab56399a8dd5db3a152677d01eb8ee98d1927ca5069e0d1ba3907971a2199ba3634b48e570dd97d93729a6c43e4f359e2d89795218d52270a338a1f511b1f008cd04553c1a89caf987fd18c329be7ac2282084ef1789615d7eb7afd2261f606d3953b8863abe57796289e7761b01c3ca0faf2291287f9ad7027d7f0876b5f77d2a7f87dfa6ad4db905d4bcad042f403824aee3b4f8d7b5ab027fe1eda9d683db24f56f694b0b10ec72ba0df40bdd6e52b4a7b8d064ad46c7490c4705c14b06ef55222435d2d6316c7dfee83d225eaa431c11a4b85b0bafbe66fda1abadaec8eabcc2f8c688a7b9cb2942597f20cfafd7520892c535bbf6359a6989a84dd89989d95b8e5222c3aca2e8b0f8881d759e450466b75b5f36b7b723b0a212edf52abd591e7e545a3974b8b31b84b523af7b47e3804b5a268c86ae0bd7c80bc6b578b79f749eccdb4a00813925ef40259ac10bbd0fc4f2fc536c30f7c1efe68a52bee22f57021d23f445211b36fee6302202c9b62c6cf9064a2df424563f9f805e51c4092482253e4c258d53b80a2d26eec9fcdc104f7124d876a3ad573a7f419b0d67a41a34dac9d8f28cf9519b9c2677c9d1e720667d5fce26091d64c6df8b46c98b58017de0d055651e8caa3a230f57aef214ae2f27fa85400e34ce7087538fb6b854a6ad534780052210b8b8c90b4de4c2afbad9f58a71770ec186cfa44b61b53876bf904972078673845ec3181caaccf11f71a8e2a502deaf144f16df1df3bd81277dfbf6e1ae5a17363725ec31759b743066fbd4cafb5eda6e09418bc375f42e0dcbd4624dbe26c5fabb77152124f400e677004fcf3c862a9b5576140cbeb800fcde4409caae06286cb643842687bc6b89738374c7c759c911d7bbec8613f1fdd996d4b970fb6f2a9290a84c34d5fea6b8006357c8e6e9d4048d5a8ec476dab0b55e8358ecf7d27c31da86681f3fa74d072b1150223eaa21c4027378a99c8a2dbdc07d4062c401e92eddfff82841292bda2798c4e2ee9e09f618cc181c91a4dbefa44c410dff5cb705ee005e3a0470c13baddf9066109797a3b51e73a0ef229796d330aee0c0160529a4ca3b39e861ee5c4a0f78619007ecdf32266c7c42f0c972b91cccc45f6f8688b2692f298721cb5da39cbca9a5adef6969a2592ba421680241f8a5384bb92e70acf79c2f41d1171dfc6e1939887b9c8ea94429bcbf3532919fdcfcd0f443d3c95515b41e3f9c84bdcd3de1fd481f98482f667f2d017e3579208341e9a225f85516c8ca133cbada77598b6f596e6151eca377fcc8029cc99a879b26d975684173c0874509117ece4136bd2d69848f858c05e8ba3421499d7fb5e3e7645fc135117d8fdd1dc46bebaedfbad4dc7cc23fad6e696fe349712cf7579b4e63b38cc7d02a4c6a33ee4117d7ccdb86ea02cd791756b2a3c516d59d39ed83a8c328823f1934731820c187624219b487ca86edcc2f61a064e4e8d17f58f4a71462f3f0cfef6be95c0eb3737616de5954096d761a51534b36d798c651541acfe2e5bed6e58c45c46e014923a342409d49e782054f2a4877332e0adb0663dce0e84df0ff0d71c4c5ef18d9cacc8b8d47c78d53fc7649bab719334601c79a345d2101e65d4c3f1616741b24fdd9f7d6569fb8de67799648b323856c2ca96ac91823aa12249934c7050505258c43763ac9b174d55fee71c7817d65056c30d7853500656040621eff9f291e9db198772451cd3d58bc9421f6191a863777edf49125543b22ba127ea3063e1989bc4635d50bb22270948ba594b2066d91e1589aa0c1f476af8b170820ae0d2409df83819579621161ba55cc8d020e1ea68cedbccc9a737746d824b06e12cd2e3730260fdc52a68b6e142bdb997d8f93f25d241360285e7372939828fd54c015ba90bb4d553558ffa2558a43098644357860785addc455bf1ef4ffa5aebb8f002eb0f6a8ddd6f45edcc7cfe88d7ae7141b9113a4ab851b5fa8ff39c7024d2b3202841266f256b1f4ce4e4ea8b83d0067555ddd56fb1f8ed6cb5ce7340cdc403fac1dd3b48c373629202c70f1f95c0001b4f94a9b4bad9e1c64429b541ff57a2f2ea0c1fb2af8f0e490fac415cc3bcdd5739f2069e2bfb873206b8c8811619f1db39386cd2dd748c6301239b4e873f3d4618a43c52ef7d8abc0ca127881dc8809dc20a8730718c64c5a132cf638ec1e3b3cedaae55763637b4c6341baeff17e14c1e3c34e5d23befdddec0c7e25f0ebfa90a0bca07a0f5446507b7fb9cf91cb3cb8d24a111f46e7dbe3ea62b384eebecaf8de49b15b17f9cf151c4c3d33220b3adcf9f87222813ff2125120e77c638ab19c1daf9631d53b3efee67c3c40c9c23495848d4c5ecafc0998bf2704bb78ade666f414e9c6bb100b90451d93397d062b741a3ef20e7e6ebebab8238e2adf415775179c866eb4a2f628924674b20f711d2515d3b024e7e0fd3af6e116cfb0030709fb9bed4a441646c03dfa0b11cb460415ccb3c0999b738a0b07d55be741f3d2646a3d9e22bbfeb46b87acb6fcc2d83cdf44b656d2269a0734259a738d83a7429bfc99da7238b9497fbb10e0f623dc1f793e3be2326a7e55677e20ebb152f71794fd70d7698d0cff1334341b3a8eee38d66a021cae6f8dfe67913fdeb8d46efd9b77f28950aee7ce112f8f0dfc04132a99d95dfa923d391e06a578056ec8d15ddfb0fe8cd2790ed55f8254c6351d471c7273c2e61c2694bf6b0a1173cdc2d422246ca5cc51a9b8669dc331fe33bfe330d5167a244cf041f8444775562eaa8ddb92e0623d4c689db0919ece87a7feeb1ddd2d034a0d884d6bc8b5fa5fc7df00cf667dfc82c9e98e9b57fbe07fed391729034c467b46d3d16ed72c8ded069a117ca2d28e71ef4c37f1b1939590af19433ff2a784ff38134aacc59719673c53ed047d79e0c36f3acc03f64458826107853ff8d02de9362ffada79feabc597d019de6afb6577dbbefcaeaee801a49b1718151690b43a4d70b2fcb73ece3e3ea5fe2a9616cc6a9a494b8c6c7121497688652eb854e7bae19af3e5ae1a8df75138ce18621d11f15054ac8971f6ac084bd90e3909b46db321916c9e3c8950f3cc8e0a10dd255bd6b359c73dfde7de6e01538d03609253968177af14d327337bc1bc4e866c75ab5b69125f9e9a758f061418110a615b97f733f899382e2a14fa0ff0d1bda3b7449e1f54362697aef064fc9dd50692ad2b9ec286a1b7c1f845b09f4b1ccd46142f9e272d018eabf76c23c26b1831762a8a2c9152ef0cc843ebba20bf979e851e76141f41e817cca0ac60c1baf239a6c920a244b0be031a3b65d76eebae15bd9d49c53dd3e4303ca379f26840901f1da886d330d8ab4408a25eaa280ddb545b968b4aa59ca6f9cecbf82984264a3232b97498be0729f3d97dbd64823393b42794ec1676244212af382c37d53d66019a5db34c3d8ac156cac852ecb1c57f0f6057815460eb9f666c368acca6dfee0d2b7bdacd2148d74cea3bbac8b528f00809f403b1964b9d199d2db93363a53732efd97c66f28cb51442dbfb1c912dce078944e02889f9395ff2655e2d7118a11848e3ca64efb5eca119240b537b5d4548a744d96962e9b2a99af73bc4d753df16fe75a08005da7773b7bef68a44f56951a3a93995c5d0516a8b2c39abd2057b3d86ba7fe511dc33292596fcc4d3c67df88812e26d3628527752a81c8ce6eba711ae7ea8748229caa2580e18937f45cbfb0041f0a9254eade41c95ef4f390db6174e6b0ba89af344c215393b627348ebf00901e343c1599c832bd2cfb751750808719f18119ab3fee6baf5fd4b8bf7c92a271ff24576984610abaf1b2297c18a2784f57d7aad26f52c0d60fa6cd2484492a1524dd14924eebf53348ee2dca0643ef47381541b91d1843817067a94bb7c79e4e08c4d81c7266afefeda4a0db21b0db629cedf13b4f6ba11e6480116a7c9780009ee0e1e2bd543dbb68bea356a6010a1e3ded4229c4d8035420833fc83337dce4bc483eec76f479f33be2d6a8fb013813952eb66bc247f37f9def7e9bea0f277f380079aeb48c192b9c2c24f909435586bcb994ae6f75eea111c8842b855f90df21e956f839aef89bb1b96899e343cc66814ac08eed4defe5e2a62c54cf83a27f0499224e02bac652aebe6529143372c483edcdbfbdd5a02106fd3651875f23724d2154c0138ace745d038d6cf6b233ae2bbb8313c8ab59dc896844da5a6610845d9147e57518ada61b7debcd66111d609587632cccb936751d099639735f09f77b2b9aa1177e95ac0f8070bcbeb01bbfb3e7a638811d919016ed628865e2405aa17f5129ea43a836a76ec7cbd943e20b1e388d47db2df3394785e27dd7efbe350ccec6057af7483303a6a588d4005c05ff7f3c90dc5b9a0f95c3c541ea40615f6b0ed6cd3787e608e7612b52199062991a63511b3055dfab25b18ab55948d9d529aff91a0d8aa7705afc187e32d1107c07623a309390f32c62e1abb830bb090a7dc4c6817b554720fce354563662d78cc2bcd0483149b304ab86502f0ad36ba2c262fa60b1b84be2499e56fd72129c0fdf1961214df8dd6c9b831109fadc7464df090aac96d221ab387b6eee3080ea0d94e195ca795b5d80c42946c0c353e39075bb1aa4f3fac36a77445cd57b11b3c7ef0e89aca911be35fbb72e1e2fff84b7ece66bb8b3dea52b0aff473475d298f730e79ff77e955856817514908baaec1b53f39f9d5dcbb1cef29536a483a1845d8538e5ec43f634914bae4f2319579adcf3f0fbb151c80b54f02be54be612f9417f916579863dc16da5af07fab05db2094c77612b445b7b9784198abbfc8387486449969118d3a83142022017d69b939d30fd6b1c5ba360c0e37f264b5d604c7ebdaf327874b22d4092dcf35aad93f0f0b3037f90000d4a1892dab827d3c2f1ddede948d330706ef17bf442f8228c168401541ea89a837f81cfffde8480e1b6014f2f5d3132ee740e04622600ad93dba41e39267f17554b29102390f8196094de19e8d966d3c03a9e3348494b54045629224612734a1304ed76d17a465c305675bf99b7aff1950dd2012e6cbe8d15a9e35a7c16081bf647e2766c7b8e9d12ef8a2c94fd7531f6d1a7ac7ce6bac028917a2a71c8eded4f3ce3be8c7d98422094e88414338fc25839a582b5965c71179ce3ddf7a645bb163f3265f4bc4ebe91ff25d86520696d237741ba73084070e23494d6a890e1ae748091a6936a3137d16dd14571f86ee118bcb17c8ecd2ed20c25cbab79a78c53fba1d5db4fdb15e6a124bdb2d41fef080e7318f78c67451beff04b58a182f592fec3feab6011be4663dca8b62ad2a8e40c716b3c8a170e1574517c44e146109d223529b0de480aa168b5a4b0522cea03dd26a684b08c58aa2e1fe41f44ba8837737a74a4681b0508d9604aa7484c5ec68ccb369c056702b03f75722033597ce5b55e5a88f14e3ee23b1c96f5d38604128cf2e901a745996f4a29d412e5ce1b4ee82cf2f248aa80f8c8f2eb7b6951f1b0910857a17edc319bc76b1a59fe05f9394a5b16a91ef15afba9ee2e115c9c9afc51a8d7c4a61cb335724855007f564fec341342d53717023118917304c2dfcf0bd79e456c94718b8388767564d6055aad1d505f36506fd6e8cb1a10f6fe8a67e772030dab78b43ec15fae0322fe991944be86cd0bd78ba00110474dec84dd9597a602c4a6c97a27f13159ff1e32fa63e28f63278b09c9d051cabf8bb49f2780d4c33ca061ff16c6288ff29d46a22e31c719dfaf6087590ea8197c066f9057e0c24b40f5eb205b730985daaf7e7d85fecd7449677c0ec1abd7111d795de6de4a6ab30cf266987dd55569f2a705ae16f51de738fcc4cc218f2cb9dab8af509ba26ed93ddc7a73028d9187527fa6d554b491faade05852eb50847affb81b58d2501989d5b4a9de4237b33bea154c4d016b08f3e1c39ee61382fb500be2123ccfcd30e37c88979a19d636c9dcf5fcec822e5558cb2939e40e1af039aa942b503c9fce95f8ff51a2977f58e4d5032f9099fb4783b7764dd1299a1a89f21c1f5e38038f219e9a4e9d6c6edaef70132ee1f2a580227b256c9139d339183e278277a9dd260bf7d8a9853c4185f9b6509ab9b67a3563ed0b9afca6f2db28258025d8c2036cc4cd9fb4f7007dca96597c26476def29191cb0c77c44df80994ad3c79833820ce2f1108d17a083fa857397d2839c37899ce2e634578b3eee0135f66cfe1aeb7bcb8732fedba16093a6e666321236f56e285058d00c1bba2db3263844e4b0ea013e7a8d370ecae9ac7a9620eaaa244b25be1eb6e71368f550736d3ca13747edfb6254c9a373aadcc30a148907d39e81bdda8a53e8644b39a1625de851fc5d759686c63c76bb162b244415175eb0ccaf3a63d4ad910369e30313bdeb175ace63ad33e400e642dbdcb472a336d82d2f818e6888e5aef472d03fd298a5d14b8da432b72e6c5f5694887a7e4e165bc41055379bd204298d65796d5351a9121f949904bd6c87510649127ef5797d432c6c759939c99026d57076db225d7d0df01ab9b9e3e1221e7e323ff752eb7988cd2b2bbfc8ae7fdd23c3f7cfd2ae2679014e986e3c01b6c66a67fbf69483ad3e89c1f2dd0867f411151e048d81212c0b5e2fbba9d317f283f97c9576180121f41856355b906a22c69869bc27729db5974c5b1257b35d186dcb4e9f7bc51dc331c0ff9baed5955e5052a6ac7743b"
let x = getShieldedOutputs rawTx
describe "extract Shielded Output bytes" $ do
it "should have outputs" $ do null x `shouldBe` False
describe "succeeds with correct key" $ do
let a = decodeSaplingOutput (bytes rawKey) (head x)
it "amount should match" $ do maybe 0 a_value a `shouldBe` 10000
it "memo should match" $ do
C.filter (/= '\NUL') (maybe "" a_memo a) `shouldBe`
"Tx with Sapling and Orchard"
describe "fails with incorrect key" $ do
let a = decodeSaplingOutput (bytes badKey) (head x)
it "amount should not match" $ do maybe 0 a_value a `shouldNotBe` 10000
it "memo should not match" $ do
maybe "" a_memo a `shouldNotBe` "Tx with Sapling and Orchard"
describe "Decode Orchard tx" $ do
let uvk =
"uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm"
let res = decodeUfvk uvk
let a =
OrchardAction
(fromText
"248b16d98dfa33f7ba69a0610a63b606699da76c288840b81d7691ee42764416")
(fromText
"17fcc27cce560733edaf91439a8020c4a029a4e7d5893ce024d5ff4b40bbd0a9")
(fromText
"34796d541864832acca43f083892e98a46c912802d5643672d3f25bea177c61c")
(fromText
"a6d2ca10e3fc7446e372266ef45ee3dc0ba373bd378e6bf3092519a7f272bd8c")
(fromText
"08beafdf59110b5d045e4acc13731ef1a27bfa3a9cabe1d575640c18f18ee6697fbb132d36e982ae3eadf5f37fd35f42c2bb07def14759deab1fbe2f98dc1d5913e4a6ef388b714e2cfd6d89ba2302800e02ab5f45e0e02e3895448518cd8afd2c37bb48a66d8b988a37de9d0838d92876894a311bb9f314ba842e5c18ff7a3d8c7f0ff1a7209e2d661595db8f4a4aa267b9593258914bf63c09286eeda7c9b27ddbb4646208c0d03a8fbdc5d96633335a5a65316f5b25189bdce735bdea7e900de56d3b475ae51b7c35eb7ae79ba104baeb0a5a09d1cd8bb347ab34fb26d62ddbf024f5394710626ec0a665b9c917e65b00256db635145164a0329db7bc5358f435d573b2662adf8a6128801825ec8fb7d8aeef567d35c875ddd784fceb7620355e3f056a648b39b4b2d29a1f5e7b7c4ec5fd2b1874ff1e832b308f8644e83878d90582b9a6fd6c293e19dd3e24dbe1b4c15c96608169843d46551900a8cb787b15f0f1696b736dd4c8ebacf1e3288b14e469bdc004fa8557d6b1395700eaba59334906bb012f876e4cd7acd2157719ebd2e28bd0cd4ab4ac458f8848e1c30e729803dd47102200fe703932a15c3618862ec83b40d3aa0ec2343641bcb9afbf931ab21aa4afdbe7e51deca24283c2ccab0eef6e07aac5a4bf3a775bf7d2ddfc8d8766c3bf8e35df1435cf515d93c3b9549477bd9f53d133f05dd256fbcc0b13a63e3e7f8cce6301ab4f19c114f5af079f8c581537458e861b553218a890ea3e77fb99781c7088cd43c67c155ec611c1148721cab5fd0168e4a5ec390b506ec44145474c")
(fromText
"1e40d33446d9f0f0fad40f8829c1ffe860c11c3439e2c15d37c6c40282f9e933dc01798c800e6c92edb4d20478b92559510eda67f3855f68f5ab22ca31e1885c7fa9d4c9ebfb62ceb5e73267bcad0ba7")
(fromText
"63d0d6e8e92691f700bf8af246dcd4ae1041b13e3969f7a9d819a06e0f9429bc")
(fromText
"fe362be160accf2794841c244e8d80bbeb80b9bc95bb653d297a98d32bddf5a05dd5f874891d55924a83f722f75f576f63796770c31074067694cffb2cce7a2a")
let b =
OrchardAction
(fromText
"8921446787f1bd28fa0e4cc5c945ea7fc71165a25f40cd2a325dae0c4467d12c")
(fromText
"240b08b7861aa78989c68cbedd0038af9b3e3456bdc7ff582d597df571d54da2")
(fromText
"e1bc8ccba69ab9f429bf735417aa005cf439d27500b0d3086dbf1be764b42a36")
(fromText
"c89c58ef8553e7d09ba4090654edd1a8c98763c44d3dfb9dad18286c7ef363ae")
(fromText
"0eee1ca5a3a4959cd4b8bc277e6e633f950680c4acb978c14ad8d944a784f46771c9d666a203ca3ac693943d79dd23f8b76a734a62e81932cbe98e8c851f47a11aaef50249e53151f38f88262a4bae8cf26f5f8b2db1d165aff9b57b64713a677c167608585c038e34ca7bbe468e5f86475ccec0a4a8b9a43b56e342e77a6bd09415787c9f4a1c6f20599f57545f1ac32c3a338d7a5bb2d35456adb880cb65c1455969e10df5d94b8c74b244e7093b1a88cc10697a7c2f4d34b6eae3296e64b820573b4d52e06b4427af5b8f5d6722d3a93fd85da615fceac732976ad2c1be4150b4821c149521f5419ea0746fb132d47f593cfc8a3aab6b2b4480c12fadf21280ccd3142e7188d9e5aef3fcd8c5dc0c066dc975bead023ef7f89a486b615b146110ae68b703a8349a5fc225b26a08b2adaf36fb44c9ad1be59d7ced134eb84e3f0b4aec19b71b2d26e910628a11446b97c5e6bbf97e1befa4e04b5947f83c65161b92f58088d28e57adc2a2873e27008e29772c5803502842045cb355d1ea5a9d27c2683dcb38cb49d26af39625ba99b1342f700387b939e7ff6c129417ca8836fe1e96331e35c8bc0763879e8c17cd4535fbcb27a2785c0a47294e07cb54837bb997df34882ce0bececc6adca365c76fc7533cf0503458937dcfb6058b016dbbd399b9f0cca44cbc881016f4957b5e10daada3393d5b2a4cb15ed983506d4d264f9855ce2ef87a7d4a1fc03293a22c28a53c4455447d546813fa33008e5d2d81848825fae2f437ab9575ba99c230e78f4b23e575e7647beff0e4c4e2b0a1f7320e9460")
(fromText
"d727aeec27bb0f7463c6ed4f5b3f4085cfd3e7218478db0dcebfca875e025320fb64bc4062251823859e963446cadd9924c559e5f981480df5a4f036daf5a8033d4c8241e128902aa1aeaf6adc149730")
(fromText
"98e72813aeb6ea05347798e35379bc881d9cf2b37d38850496ee956fbecd8eab")
(fromText
"cb9926f519041343c957a74f2f67900ed3d250c4dbcd26b9e2addd5247b841a9fde2219d2ef8c9ae8145fecc7792ca6770830c58c95648087f3c8a0a69369402")
let decryptedNote = (`decryptOrchardAction` a) =<< res
let decryptedNote2 = (`decryptOrchardAction` b) =<< res
describe "First action (sender)" $ do
it "Decryption fails " $ do decryptedNote `shouldBe` Nothing
describe "Second action (recipient)" $ do
it "Decryption succeeds" $ do decryptedNote2 `shouldNotBe` Nothing
it "Tx amount is validated" $ do
(a_value <$> decryptedNote2) `shouldBe` Just 3000
it "Memo is validated" $ do
let msg = maybe "" a_memo decryptedNote2
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" $ again prop_PhraseLength
prop "Derived seeds are valid" $ again prop_SeedLength
before getSeed $
describe "Optimized spending key tests:" $ do
it "Transparent spending keys are valid" $ \s ->
property $ prop_TransparentSpendingKey s
it "Transparent receivers are valid" $ \s ->
property $ prop_TransparentReceiver s
it "Sapling spending keys are valid" $ \s ->
property $ prop_SaplingSpendingKey s
it "Sapling receivers are valid" $ \s ->
property $ prop_SaplingReceiver s
it "Sapling receivers are distinct" $ \s ->
property $ prop_SaplingRecRepeated s
it "Orchard spending keys are valid" $ \s ->
property $ prop_OrchardSpendingKey s
it "Orchard receivers are valid" $ \s ->
property $ prop_OrchardReceiver s
it "Orchard receivers are distinct" $ \s ->
property $ prop_OrchardRecRepeated s
describe "Address tests:" $ do
it "Encode transparent" $ do
let ua =
"u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x"
let msg =
case isValidUnifiedAddress ua of
Nothing -> "Bad UA"
Just u ->
maybe "No transparent" (encodeTransparentReceiver (ua_net u)) $
t_rec u
msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"
it "Recover UA from YWallet" $
ioProperty $ do
let p =
Phrase
"security expect junk hour people bind law hub between topic wink cliff spirit scissors auction idle figure option wide useful swift prison cushion round"
let targetUA =
isValidUnifiedAddress
"u1qsylqauvnhw8tsfe3cldcsj3mjrfqzgaf3mt8yzlkjuvsf5wzj223yvrt8q66qukfqcc80x3z0mk6ym6pm2f0hukzkp6t4wj78h85t6kfr2u9mqsfhdd73g3sc7ezy2ut3rtq5jmejatwv4xqqd6l8tt9fycer8kdw0gz6e607nkssqsc7kd7nk2yfz2hpvpqhdg39wxalpjzhe34j7"
let s = getWalletSeed p
case s of
Nothing -> return $ expectationFailure "Failed to generate seed"
Just s' -> do
let oK = genOrchardSpendingKey s' MainNetCoin 0
let sK = genSaplingSpendingKey s' MainNetCoin 0
let tK = genTransparentPrvKey s' MainNetCoin 0
let oR = genOrchardReceiver 0 External =<< oK
let sR = genSaplingPaymentAddress 0 =<< sK
tR <- genTransparentReceiver 0 External =<< tK
let newUA = UnifiedAddress MainNet oR sR $ Just tR
return $ Just newUA `shouldBe` targetUA
it "Recover UA from Zingo:" $
ioProperty $ do
let p =
Phrase
"cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest"
let targetUA =
isValidUnifiedAddress
"u1trd8cvc6265ywwj4mmvuznsye5ghe2dhhn3zy8kcuyg4vx3svskw9r2dedp5hu6m740vylkqc34t4w9eqkl9fyu5uyzn3af72jg235440ke6tu5cf994eq85n97x69x9824hqejmwz3d8qqthtesrd6gerjupdymldhl9xccejjwfj0dhh9mt4rw4kytp325twlutsxd20rfqhzxu3m"
let s = getWalletSeed p
case s of
Nothing -> return $ expectationFailure "Failed to generate seed"
Just s' -> do
let oK = genOrchardSpendingKey s' MainNetCoin 0
let sK = genSaplingSpendingKey s' MainNetCoin 0
let tK = genTransparentPrvKey s' MainNetCoin 0
let oR = genOrchardReceiver 0 External =<< oK
let sR = genSaplingPaymentAddress 0 =<< sK
tR <- genTransparentReceiver 0 External =<< tK
let newUA = UnifiedAddress MainNet oR sR $ Just tR
return $ Just newUA `shouldBe` targetUA
describe "Sapling Change Payment Address generation test" $ do
it "Call genSaplingInternalAddress" $ do
let sk =
[ 3
, 183
, 26
, 151
, 89
, 0
, 0
, 0
, 128
, 199
, 189
, 33
, 169
, 114
, 194
, 50
, 0
, 139
, 140
, 162
, 100
, 100
, 35
, 58
, 226
, 6
, 47
, 232
, 34
, 214
, 11
, 173
, 142
, 40
, 45
, 163
, 190
, 207
, 49
, 130
, 158
, 113
, 232
, 251
, 79
, 98
, 77
, 195
, 196
, 40
, 42
, 113
, 133
, 35
, 211
, 68
, 146
, 104
, 5
, 56
, 244
, 162
, 55
, 239
, 55
, 112
, 37
, 38
, 189
, 183
, 121
, 201
, 1
, 60
, 158
, 151
, 141
, 123
, 250
, 95
, 169
, 123
, 208
, 56
, 103
, 74
, 85
, 49
, 152
, 207
, 245
, 216
, 58
, 37
, 0
, 127
, 186
, 245
, 234
, 47
, 68
, 11
, 78
, 76
, 12
, 171
, 37
, 63
, 172
, 90
, 111
, 94
, 88
, 152
, 211
, 53
, 243
, 142
, 16
, 195
, 142
, 50
, 14
, 13
, 32
, 91
, 5
, 82
, 182
, 121
, 136
, 109
, 125
, 165
, 125
, 123
, 226
, 54
, 60
, 34
, 62
, 111
, 167
, 88
, 254
, 113
, 204
, 47
, 181
, 97
, 18
, 220
, 46
, 51
, 160
, 62
, 16
, 199
, 143
, 184
, 200
, 209
, 124
, 154
, 175
, 29
, 216
, 48
, 201
] :: [Word8]
let cAdr =
[ 31
, 232
, 31
, 17
, 196
, 178
, 208
, 227
, 206
, 199
, 105
, 55
, 147
, 23
, 151
, 206
, 117
, 59
, 249
, 162
, 218
, 140
, 189
, 17
, 60
, 116
, 106
, 56
, 64
, 203
, 152
, 52
, 155
, 133
, 179
, 118
, 47
, 161
, 70
, 155
, 21
, 22
, 41
] :: [Word8]
let bscAdr = SaplingReceiver $ BS.pack cAdr
let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk)
fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr
describe "Zebra response processing: " $ do
it "Read block time" $ do
let blkdata =
"04000000c732575a3e94b7464c84b35935c2fca26e40d37b6f3278bda3d941877b192a0048cb59da8eeaf9f622d4537ec2a27918415f444d0bb143e8091b470a18a438c117fd96323fd1a9c775ed793154de0e8a2023b551431e89b5ce6f30ef047388f0bdbb0266fe81481f2c006046a8be3bca5b8819209d053a88184bb48753171accb807c38f46d00000fd400500b6a304cbc4e6caecc9b3a29ce42d71752bf5396944e28465e29d1bd7e7f3c680db3c4b85a1488d7c4e21a70c1cf50cfe4e7f70a4f1abb638f144afb90dfa69adfc7c981fd8792df19926da695356699ab76213094431bf6491b80572c310ab8e51c27cda39c771371dbf7adc60089ff5f9614577a92ce3a1e452d5d8280e3d970322e1fc5937c53369623d5e58e57672b48c2edc7a86f7d7de62e06668097547b782a01d3794a50f131695f7de3e4bf038b12abffd29b8eba618820e143ba0be57ccaa67f64511460027efb065c6ddff501376744c6b68ca01d884025e7f939521d230b1a46016d66ad3ab2262dbd477b38bbec234a19425b939dc0f1d868035c3ef99eee635c9dfd62235ede6d62f652fec499127a152e7be4f959a9bb7fcca9b335de98d14aee1330fe37f1ba28be26902a1cddbc3f67f2f6317093d3f278c1c0156e5319a0b35360e38719911b8012a8cc34893f05dc28a22787f3e0461836aef602110e621c8b9d78e991ccc0160deb8c8921f6fd13bd00d47a170b9d0b09ffd57e37467400c138823dce7c12027eb9f919b8d5f4072b331a042f6f5ebb814c6a903110cc97161787d1eb2716c8fb98d747abf321a0b079f3b6a1a0329337b4922a0d739886a03f0d475d41005733f26c675dd9694712d6ee07772a5e8f6b5871789ac14159beced65d3b2196725baa4ce04f059a7034ff0d7ce081e94d23e0115b9d8f0d99fa5b4f9771b4feaac881ac80f178c3575cc70858e40f53f1d0406253edba75e2bb6f40eb2ef4ece3df2b8dadbd3ac168b1743a0cfb68ba81ca4c838435002587852e81e0a0c3c77e11218df892ac0c2f343967b127fbb822712ceabc459ded7f3ca3e95ed5ddfe6cdfe5657da1b12ee47eb63af7a839cdbc1f52f51e349dbf2bd7d8529f814108723d97fcfda7563c4fbe3a24118352e4706f9f91406f11acde3a570e10a258fa6a5415f57a26988249a87e57f192d0408b6f56c2ef65d5e37597d144e8fe1014a4f552dc8c213b0a70b2ccff6747f6f1e0d598715d8cb0da37e0331c01da1f9e0505e9d1907628427d5d032a29fa0e14b75289b10c7d3109b590aec2a4debda9eabcbd3163f6df67efd92687d0c492a71d48b2995a658b34e96814e5674a62e3738efe53e0daf4bfe9d1031e45a246bd3b32c6d581b71c0b60ad711570bca3848c38ba5aed74d977a185d199db721c2677d4868e5afaa6cc2c7369898f58f93738681118d529194d41f0cb6103915d2e99422db387fb90046eae764a79dfe8733a7f68439d0f37e773d16ac7f0ba3a8ce0fd6746d038704b2a0a979659a859682581fee0be3fc50eed13e85e6b52f398d16b59f5f738c1eb289b3861d636b961d9220c1f76bf8a8be3ffd1e2472461202285aa1c5b7eac58564aaab0beedb65e1078517e031888aa3cb8843876aaf19311d5150529a10c9d9bce1538f7533aff126e518ef1ebd919f26aa3869ed1ced167cb1de6075897ad2dc461b9156922c4ee387f8e1635e95564d3964e47d39e6e82079e0220df00614ecd4fd31a5f3d13c9191fc65037ad3cd651f055cc795260f21bb0ba8acd7e3b65b5ff81d5a1b187a777c826f96637e00f8147c4bd43753fe9e0cd73564b53a0e9eab27745505ccd2e4a1a217307c66030ca115580ce6ecb9a8b043ca41bd2de15abd13838d130d3cfecd8d0e3bb6c982bf1f9cdc799c3256e2d722d3f13cd7acdb9d75d7e36483b2ae01e5dcb70db824eadd9c3955d79557d612a879fa6da1da479807640e37bf25a22091f1cf6fb5293e60e5616416fb2adb1d9892258e8ac6859978159338d3b6e2063b9d842a24d61d0616506d5088fe76025df35619febd57f92e30699a0df8731bdbad0775922bb12fb1697d9da833010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603204c2a010cffffffff0480b2e60e000000001976a914278aff0c0f8734638ce81aaef4ab0afddd36552888ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8740787d010000000017a91471e1df05024288a00802de81e08c437859586c878738c94d010000000017a91493916098d2a161a91f3ddebab69dd5db9587b6248700000000204c2a000000000000000000000000"
getBlockTime (hexString blkdata) `shouldBe` 1711455165
describe "Raw transaction from faucet" $ do
let h =
hexString
"0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02"
let h1 =
hexString
"0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000"
let t = readZebraTransaction h
let t1 = readZebraTransaction h1
it "Transparent output is identified" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let a =
"utest13dq4u4dnf3yddw8lq2n6zdclshra6xsp8zgkc5ydyu6k20zrsscmuex46qa4vh84rgd78sqnlleapznnz7mnzx9wv0unts8pv32paj8se5ca3kves2u4a89uy6e8cf4hnarxydxh7hq2e9uu39punfmm53k5h45xn9k3dx35la8j7munh9td7774m8gkqgc4mn40t69w20uu2gtks7a"
let a' = isValidUnifiedAddress a
let tb = zt_tBundle t'
let myTb = fromRawTBundle tb
case a' of
Nothing -> assertFailure "Couldn't read address"
Just addr -> do
case myTb of
Nothing -> assertFailure "Couldn't read transparent bundle"
Just myTb' -> do
let pkHash =
BS.take 20 $
BS.drop 3 $
(\(TxOut v s) -> s) (head (tb_vout myTb'))
pkHash `shouldBe`
maybe "" (hexBytes . tr_bytes) (t_rec addr)
it "Transparent input is identified" $ do
case t1 of
Nothing -> assertFailure "Couldn't decode"
Just t1' -> do
let tb1 = zt_tBundle t1'
let txInHex =
HexString
"Km\237=.\228>T\DC3\\\EOT\249\163\ENQ\180s\215\215A\187\230\243\131\170cn\ETX\233Hp^\r"
toText txInHex `shouldBe`
"4b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d"
it "Sapling component is read" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let sb = zt_sBundle t'
fromRawSBundle sb `shouldNotBe` Nothing
describe "Raw transaction with Sap spend" $ do
let h =
hexString
"050000800a27a726b4d0d6c200000000c3c02a0000000111ac7f9600d034fec892f50600006b09ad88c990a8f3c5bffb6856beebd0ce356c5d1413c63a9a88db71c3f41dc12cd60197ee742fc75b217215e7144db48bd3fa55de211e0c8d7da70aba0efe17124b5250fc9fa095c8e50b53168142eb7ec402289a84ab762ddafb0f972fc07c61eb45249922752dfac17fd704a3b6156a6955286c04587f37fc781a9a0b28e92664dde2300c6321dd21d367c16b4c2b510e5d0a4358624e1ceb816da155e588726f783ed6bf396eaecada3d3bde7a0722419014c509a7fe264003af5388c5c8cdf2076137b26cf66383b41dea2af415f3d16767124616609b96789e2811f7bc8b36c0b746d225c0aa2c98b370fd39b2015eea43d523e5fbccc7ca127e59bc92ac8ad32d05243d278a26f07f33f45df23c3e8dd40d72af35a70068cc3f8445478c9cd3a56d3515e207a1b95c39c9d8b45e66827cf63e6018bf1012a47aa23ec0174735949f62036df3f677f92d3aa10365e2d358d92f77e9a6294c245eb8536493e8480f5215cdf7c863623727efa188fd8679f95318d0072915dc2be8e77b5778c48d3a2ea614990984c29dfaf7c582d269f0e9ffe6e3c475c99dbf5f8756c334b3c7b5954d68549c65fc2648bb389ba63397525829c48f7f32e43e866dfbc8ad86f54a867e592524f9c43b007328b78220cab4537c2d65f64c819ecc19ae09e17b3c84208cc169c2eaf8831d452dc83527a966b288aacce6f261aa8ed23e2c0613c48161c1eb0199915ee9c1f9cf0e7e5445539f5016b3ff701408b45557154a53e48998b49a2d149a67202caa6b585348b42fbeda770e084128760c742537743ba8cec4f997fe575db9cb370d89278c607a598ff06f1ece047855ea0a3d808d84272e22bcdeb2eb5bbe5754947be5d6fe42038d26f13313e039adf7e1091a901a6daa24b247b13c810eb5b50cd6040463c6bfa209a792d77eafb0f063167b11777d97ad05ca7eec1f1a45698e33f8e8b11a0ab0ad16ec10e4abba6de69255e0e00c7e712503a107012a46fcb9c5186a1fc5be96dcc279fa3dd18e69ee4a8ba75edaff105716b4751717aa818d514620ddd13a6716729417571a342dcc8a6f341d05db7614e729491095e4bcc6fbdec2908b6f74d7db81106ab2e1c0ee6217ba3cbb174232a42f2e6aba7e92eb2934dcd91c5a36eae1aac5160bf6076a39baeea84b8bad043c565eb8c4206a74b68c9342f9d7b1b31eec86bb9c5c73549313dd2cbe3bce55ecfdd952f3eb4dd5fa7c2000727b62a0faeb0fac1cf05246221c37560359c44610ee8f93cdd1179fc35fbd808152f507e790eb2140faccb2f6c9897a3a219d8752388e846c7277d82dc80850aeef2de46eb2d81563d53714d39e3553f5133abeae6c487d10664e1b10a90a5319adfa7471aa429095bd272ff4d3df227bdc6075e0cafba54201bddc44edcdfd2317f682f40a25e582273c4845b550b17724eca81e89268eafef9e98533b85bb2c54cdcb6beb4d6f2fc0459a2b36cbb43fcbab720678553fc930063c64a09445ff637c12d0ade4acdef1e19e8481a0b7c2dd968a2ceb7f50efbb76fe21f49d8829d8a26b10453626593a694586a828705c5ab02cccd60e351171aea3e1aaeda07cd960e0f5205019766ff3afe124a47f4ef16f0e671e1968e662c7974ee54872538c7019cc5396f165013f4c482d400ed4ff5fb34189c75e92d336cb25563867215dc3aa195f736e06657c9e27f1468acd580ee51ca44220be83c215c1223758a7676d376ea0d0aa74b73032b85a45f69e6f8d4ec3efb6861d5adc35f57e6112dfa7334ba6fbd64b14498a89d8596423125b6486d9208937e9aa010831edcc168be8b7abe7ac21cae4794fe5f019abe1715d5d8601b646767d396e1b7fb8ff50aa43d88a2426545270093ce80be1aaa849dd2edc0310e3cb0bc13a23108776a44e3250fe5aa89b6d62159206377e03e302e3dabff9ecd8675870c7d55e33cb915d7e73b765d1c61f9ba0d504429fe99516df0534cf6a5ad53424b9be39e4835dfb301ae5cbd9edd628f0cdb6c54bd4986a9fba65f60e833bf17b461746ffcc312d9584a55c55c42c8e760d017be56f54a4e1423648c3877f8a44e3a56c21bf2bf85550f3d8a8f040c8c6687aadb2c4d1e1613626a11d30fb25340f5f14b6a7752c5e6c7b866b10f1155360f35ddc8f07ed591fa3366948fbcde1a16c35a589457ded90b69675bad01a1fa9386e68dd2001498d41033a6effe882d9e864ea7991e0c71b3e65c158cba64014f800000000003b38927526d5fc5a1b2f16addcd4aa162272f280e977087de02ec2969f10d25ea3de9a5d3d3975b4b683897bf828be631813bd5b949ddf9c3c5b103b69aa483f5fc46485bc7a5dd51a47543f57d346e680e12d9429ef7c21cacf407656779235d205dffae23666e01d92fb18c063d1bd7cd00935f029149c7e20b3d72b1b951117f0629b1ef1e49047532a59fd8bc1817343d45e832078806eb10304b8e034fdce70464d33cf6c66e857fbda170573a7965f8eed58e6cb7edec4e9f8a666fbca0aeeca2d4d161caae23005f6e0adf6859c8f1bece409aa5d44d285a8e8e9fe86365af4e92384dce318724443e8a95cc790df47cdd3e8aa9ccf19acc5eb19b4cb517c3f63e59512c037a66312786cb6cd2bcd4480e0a21ac63a83b7b9c84d3207a6410aa51a60d15b5526e3ffb4cbb02ee9f43f7ce316ef01dd75544224481d4b8ad2a8944928e2b4432812d1b20aeae3b51808028321588952ea70afeef1709eff5a75463a2201299484ecc421c8452e1700ca9f4932b0aba6b5d46f36e80f2f0297eaf7f44a79cbd21046ca53073ee60d5f2ffe2352b2538326e3e7ed851e8a62612a9b974ceb5757835b974892b049a7a0c531390a4455657b5b5afc5f0f09f8bf838313f403c96c4c2c72c4ced6e642e86b0eff220c1cd3388ffc902af68f87a1b7e8920d7f014e6cd2694b926fcbef23dc645cd75b051add0fc2f99c0346d45c2521b27a00a13dc9754306222f54a2b4f3058e80e0821d6c78e5e80a509525631f34c343567513636855d1c318c49debc651212938c487e069c390868ce700c4d19e1e52eb7b536e1568d03421c3769b920224113bea4d345510bd0393efa1ec22b87e973c663be75c84e7278108ada31af0311796de927257f59823f1ee9955f0f39b618054476658c165ef443d169afe46007ac80a0efa3d678c2707a7a001812f02dac999796872d3328eeaab4b61cadb8be9c4ee0ff9104d417ba71076054735c12fabd5d78f6de83a57f46b8d071e3c273c60a226fa6d555fa3580002cf3b6f3e50a8f5616eef66e7bb28523858315b3cde381003ab885ece89973aa4e228cd5da0d24ebd876097e7664a2b903430963517bb895435703029ea30081a874d15f4997dec3b89cd728075a0b21d838a57052f02fad03c1a3f74427d149c6225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631585288b7ac6c67346b1d9ab1ee7d24e8cb7e35f2d2b5401a89af6a1648d19f8fcd14973c4c2db543d772b81bc363e707bc534f00b872df0e3d4c73e9bc513b2e6c49e07d4475f251c21d60cf8dc2c9aed2922e4efbe8cad80db14531a63e629f4cfb01df3f61fc470123a7caa141f104fb37d2712628648eec56759bd59cff1da5e7cac043539a2040b6d364f1449ec104bec6f487dd1f784dfe377d070fe704bcfaf4ca99cabf23f4ac1f58afdcd7c8a58ede5023034cb324722e349135c0125f49a7b4fcb22d81d641c06dd0cad8d0bcadd5c4ab7c2015ad7f98b1e4db7b89dd2aa3d057b13456f4ba3b0d52d3d0c4efb35deae8d1bfa39f593be4cd8311f6e89ac462a116ab4776576c1f87b7aff41ddc728fbc601aac334148307d9a3be7c11c4b122045ef2ff913eea3af62bff2b71433f3b65f8ea939fdc19877e98a70c8cce6b94eb2bf4d696392183f8224132b004a29db1fc0702b3c84e0d345d82cd622ff9fcf567b7dd88163ca21a32122f7c2c98c8d9d0861c02548a831e5e99a064b2be13774f76669b80f90cf8c1c0749dd646dabd68d18bb6e6f84dd8ee32acc2edce1c7d02b3b145f416b54b7bbeece8087eb03f75dd317caacfe8cb2c4573f4ff9beaa2c5ddc75b427db079c7951b88acdb8a7f4dbde154cd399cf579aa683e30bb430533b3241c8703bc6576666ffd299fb4d623f277b1f8be372f705ffe594644eed3ef2bb7070e843851b11252ae8aa8414ba17c94e63685815376246361ddc46301c995a2a6857004376bdee9c92f908595977109a0074ea3a8ea89131454429deb02d6431e3514e50632000117106dba12cae3925f260d527a7bb2cd6851cc91e5d4162a2b66b88540c1110cee4672b23b32a9269eeb86063df9d076ece42a9c7938c6fd5d037a4e4ac46942c365c75b75aa08c403690de155f96ddd23a3183653a9db614f2bf069620be62842947f6aa8abc340140aea22fa3b1fa7fb552a4f0a43fc89b3790cd3721967624451f94983d90d65063d4d9c60bad78f7bb11bdb8e6af9f806bcb34d7fdb8a2da4f6c3bc27ce4a48ad529f6a5b6e055f35c344a1dd2d6a085d1fa5bbfe9fcf6bda46b320e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e7f01c4200214e2969fb6c0fab8dc4ddb8475e28a21eb6b902d542dba5e4ad0b3b89b76ea777e6d9152feab5b0dd55f4f2d3db5f4e4013e8eb6f637f661f4f5b6ea8a73437437d14190bab1fc44df4fa78558c2b53e23283f4553101daf28f7c4ec5d9d93c47ace2ccf6d0514e0d33ea597b07f88ba4c272def6ba368f0239ba59003728fc20e9cc3e3c367a04f8faefb5a9725f0c8fa298aae306089908168cd53ea4fe769175863360f8838dbb3a185612d35b97aabd00c4fcc40245349196190a01f3b74fe6ac7f1dc854be928108bb23c67742fe4845b79b6286d99771d85bd9145b20d94b38ad9b273ccbad0ddeb5b7ba1ac322a20619b4d1ca9476ce741a0cca4c90b3b783f3b3d5064a7c15f72b97836db164d77661d415b12df263b1ad2e2efe92c5ccc50ba10c2d5d2debf89132649aa3163a23648f9d53bf14f183b920d0379ea04897a6b9245aa22ed2e4d913bace308307ea05d31753a43f62968f2653a6aad5fb483e4e78bbc42b4b853d92c81bb9c4c03934dafc8daf9ad16b3252d29028a9e5ea4278e928570c1bd112d6900df3da90a8eea441fcd1a73a1d982b010e6e7f73023dc36fbff9600f788da14b1410b31fff4ab2cd895f318aa9cc7baf9aaff1066ae4be169f663b6bdca53fa462174b56c6883e1b4b84df12b1f246de72e1af85f913000407ac30410149d1e4255ce94bc26b6ae62223f238be472f0dc9c812d137d88a7fa3fc0808693f9add9db0827d40d3dfca3dac9000a12b56d8803aff5517682e9cda163128fe166033397b79afee3f74528ccc31e3c5289812ee730cf5011ae15b0c86740644806048ac2282b7ddaed2e469c289cc70689a385e2c0459affae17a3e6d3815433363be215da2c110369a6ae12325d3c40340df4d77d09106f75d51d0c481bcf8228a4152c4dc45026f4e8de1270e04b60ac57ba0bf116fa003cc65d8ecc210e757365287203582608ffffffffffbc221f01e2f8306f906e82bb3b0952ca54890e06591275b726215e27c828761bfd601ce344fd29dfbfb9b37d62c86723a0d6a75fa6d2d8bd0c6ce822109d0ce816c8add2ea6343855d5e9ad63f3a730020eb46909af6d606ee3b5811cb807b9bfe2b2033d9ac8732b2b0eb9b28bec9eb6133f6c20b4c05e1dfa77155d289bbaa3ea6042e65326d32224e00b96f4a7af04461e1dd662284d566d6a9de2e435caca9c51fee6b665d6a1f641509b176cb29ff3fc67401e766f5ca1fab7584b36396eaba2f05e5ad0cbbba29c5372d5443f9afb4e2411f1284da03f9cbd2827ca725afe395024bc9436910053f8f8c4cef37acfc78cc05d7de0c77fc1492ef46469d93c49aadd42036c5fd6ef8d0c70d7c57e0e0e9f75ac94bbfaac9373ddc202f3393bc0f37b0a7769fca64300cd3594a29536692e575c9ef4e5dd544e108cad4ff895b24b431a86f1da264ede7b4b452e600035a38c902557b1ebb2abead1783765e778e7bec298ac9e021c82fbca82acf80094093afaa499f55a164237303d2ddfd0f8726320a0260dc2d59961c33c2d89ba40e65d46075b2d3206d76b27d00575a963489d2f168a53a0b5f04227d4cf935a2e82917c018270b7714b5598c838ed82e9ddfbdaa4448858db050cd14c500ac0e5fd566610e1d4ae2534565715cc96e499e5c190be37eea9d8fd23814658038fcc761b887568097da83232ee39b47e73c9ac854b8e70f38f6999e6f889a16ade97b9b206eae237f597f8ba0d2f2bf8f3794ea782f2264e9688a717f20431734d1bb989bfb4e40d3f4e8ccf1b8e938802a27402bd3a02d3d073e555acc5c4d941d5fbb3fef890b2e59a6a745b2e966c1903a988d586b53f63bc2a9253c7afb73f204fd3aa4a82332b0b010da159f29176ba146769d340399225d61bfad40ee21f765c641552d41b46bf5113de89318a6b0ab66ce02aea59ee302d8db4b2b1b4c71693b5159fd53627844f3b24288f585f536a52a3dcc4c71227b3a1489a5d31fc37e5a708bd724a889efec04c0c57730352493c5aead0eeb412b03a7514c24f4f3e659d529796218bb825c901da92869bc2b93afd359e24c8d55006d883e2380be6aa77d55218e831a7d5c4d566e293e4110b02ccc0508902a3d876dfd31f83b7b51b7d0d82d5838f6ed96ebb7dacf438526387a0b5053b522db03e5241b8069e9d43042922d4cb2a8c1112b05ccaae37e23511cdc90c6895713f7574f615df37c425d7cfa368ec72703a464b1d347c6db13d6496ffbb868594a330c213fd222a0ac618f1b312f4d6f5dd078ceafa20691aec35fd3b8d6479a63b82bff06a6eb619ae87ade1a5542b9582b1effde9e5d1db3c619458fab459ea2f91232089b716afa2de4ac9830830ce49284de17556de89400e2555960fee44db6379085636a5e4b938b1cd067f8d5bece171d463b432225a1290c306727187e1d2d482662c783736700be93e3f9304e0113d244e8fe4e1b38be3ec342a5a5c062cf1e20f3dc60f519947156221ee244b7e14c39f495720e4b727daf2e39c2e352b0a247660550ca8f82796db4e424be367d3d1ab70eb9b37649d9634c456d889354be44a45c61c37b5def297fc36a76d9dd676c2f643d997ab97b7735f914fbdd38f86508406a8de7dac15a1cb5ac5d9f90eb1f8b2301906c52d5c21674fe09e7f3b9c5990250efb5a890c420aeb1c5fe137755416d69aecc7549168876eb00f71865d04c946f11976cae4085eebd4e73be71ed8505470b0bfb446d735fe1afa33380ceeece76a8347e8fe2f58e34a579fd57468968d712218cbc99a68c3377a6ac542888542fc36fef2cf55c0a99266766abe6ecdbd00675c0935afe8cf8d938c1df6f6f4c849d72f354da1f09b6e7fc9b497544cf6930050d6ab8980e09feb965eebd50e91efebdb234e533372a67445de2088803328f429a8d6923899b849fb7f0af2132808544cb6bf99d0b18abe3df119bb5fdf5818e836064e5a0b329339c35ec7481628a71d35a7f5021001f72bc2b98a15aaabb4c5c462b44db100efb3a3589ec4b9f5e7900dc293443d9e655c8a50e51a00980be62f3821174c918500f9f06a25e109d3f7d63d588271a2a502e89a2d084350af0089aa4ca95be15133fac659f79df3bdde5384c2c25cdc4f94cffccfc125aba0fcdbfaa5fa9a18821f17a807aae39e4307f3b6d207a45f81877a988615473bbab0a580ae41e911fc54edd3b0cfa2391ba4c88945d2fdfd63c9a9905c2f847beb833d429af910fecd4a301e09244743d21439a66ec026ccf1ef1fd5f89d8600eaa5e07328c315205c005ed4caa4514df692a59db7b218e7543499323d4f90004398e425f77dccb3e35374bdbfc1daf35a4eed54758a1cdbe35a52d597f9cf0b3414d00d2dd5cf8dcfe51b2af90ddb2c0c59b391bead7b8fa2073061b4dd04406f2e7cda52ff000bc15dc02bf2bf22838e0ef641845bc4ce6c143d56e9a3b833e5cf4186978bf7c0e13b40e5e7f1d0ef28a3b388fa16619c13bb47d5eab57fb1c4ecc0b2ebfbf26200075b0cf5137e8f13703b7fdebd72218f32b8a0050c6633b5f1a3b9c62c83e81fc1c55741e759c1274d77a029eb7af4c3fd689331b69f80343332f541d1026a66c49fc5bd7012847d4d48be2b41064293785105872300f2ef8cd8f8b9892e96309456ecebfc893404da002867264d0a3caab84867488bb269e1ecc346d1ef7c1c76b6d16882dc5ea9dbee597dfa2d3be8e79091236dd750ad10a50e2f9f6adc1722f6131cc4986b41cd4acbe479f6ece8eb0892345662a2a0064ed74cd74957e796c4fac9c7145b0103e60750bc351f9d6d72faf2858ef23b107c07caff3940696ccf74e41001f17aa29edb068eeb6ec0b0bff10fab2571356ff5a3cff63767cad097f9dd4a5919700bd7e24182acdabf903171788800f17d5005dc07d6d5bed1dec81e13bb33196852dbcf978b45932bc28e60a83900502b11f09ff61feb208682500654cc9d2b42b60ecf99724bec398dd6829bd44a30151ab5d05c39284be657e7dde539668b5dd0b8ddcb331c4fcacb7b9c9520cc01c421c9955d7b168a9416e36c6e436e6d328d9e0fadd441fbd0d1d5bdbebbe9c19a3607746d06282c0d440ac8a243c47c27f04ac2d24620ee83fa6b1319fb4a137d1c82c436676f8d75f1b9d99b01c6f9cb4a91027631cc5717d07961179bac417ef8ae8bec969828872794cdda7b6f2e7472c50497a35782ca5841d5419ef3805c9fb30b8205f8fad2226e67048a4923de049ff3ae1f804b3ed33637c81e7530e970e2f51f9bc435e2d4ce3ebc8aea207115a4ec369a4caa7a3fc814224e8fa0e9a85f1c928ba24507d2ba16e176ad8cac7c125b5edef7d8efe1423c857949a1768748377a988d68098502720e75e138abda3f73eb9b2afdd50ef2f8d20ed5f36343fddeb59a8a591582d2d4c66ec0a89dccab3bc6cdc5aaf93a7518dd1c3a92ca486b6833bed70294d6d04b02803e4841a7faf9ab06a9c05ef672ba2b61c5e004bdfd6e490da458ccfa58be7dfef9dcc0dc79179f14659d8c4456045e48ce0039982580b9d7240209d4e5767b0bb391ef1f8929bf06d5e3c8226ecbdf8844902eab10f22b9f23381ba1b5f5c241a614b0a3cde02559669e0520ba5116f58e12fca16603e129e2e5315d581acc0d2c6ff9caaa7742b6c3e28d37f0766ed08a33f39cff6919af762d4ad2168614a190700aa87a95759951364d8445e680cc9ad0b75827810546a9510926d97835d62a65a3e5a375150b6e9a96fdca74a59269b2dd788320b96d92186f3226031684c45ab69c26cb754296a2a40c0026eb95c4f0b64db9872710d731d42c7510922dc4459600886d44586099fb957f0b6158ad703d11569699664e839d8acff3a5261f8b72cc43763f02568dea203b65be9cb06161a09d17203f168cd8469d805475dccc7c76ae2ef216e068f39ec7344f05c3d2ad794cacca891b3d0bfed335c1519203d289f74b896cb6c33c15ca0791ffeaf11c9d91b95438be68769f91d08997b77905d5a89460a3619dd44d5d92b5f88542521763c815ce15bf4f34c018297d777a602e27276021619a82a21be68cb5f4b321bca1baa7befd5cdffc80121a1fa379004f441148057d6f1f66863921c3bdd3117bb5d9ad52c45ac9a45391ec83cee3c6dcdfc40ddb3ed25a87f793430dc440a6d33218a04426b5bb7031d33b2439f01ab2fc2f64a43d2a482b1428ff5417d2d33c818166c20effbf46811d50d9d05f7ef1a37d249f64689cb37d8cbca2dd7086e141caea4c00b35286c9601f5f1cb6186452405c4a975e1c13cc10cf33e5908287f5f3fe36af89f374d172403dadf01bd06055582753b0a0a387361b91f233e6beb112b9b1eaf0a835b4d14c82477ef46868d09d03601a88f4da2dc7cef2a04a62e61311cc0284e8e48c571f622bf5ee95992ef425227d6b9272b6727b10005c53778ae7a6b155f3a7f00086e1e952527cee0d40ddd5e0caefdb19a082ea43d85dec0168d5641b570fbcbbe9058e49ed4dc546fec49d1c2041b6343dee68a09beb95df54eacdfe2792307c88b20408692eef15bb3e370ac877e927cf58ace1931e2dde4ddbd228de70fc718a5bb30d68e0196bd2d00c1a3ef138adf1640403c513c07a0e8aefdca2bfd3a9404f4c9af683bf52980c43d48912ac5347c438100eb50613a082270e1bdb9a1a2241310fcf422611ff6b67cca4c9988489d344215b9b8f8b6247903edbd5689733427c555656a916d106fe6ebba66c25c1c659d27f86b712bfac6705301f03584da09b9da428092504dae0bda0b95cb7efbed8224349462ddd2a6966cffc455ddf78d3964d1b320dddd31e3e7edd4aa40afbe0d1d01ef0b1b2eab7a266201b3ed078199c33167da82795a3dca83aa06e921a4802fc34ff1e66bf31b2e047734ae2eaf765184498e127a296c5226af7a7a03ab5815efd8ee2660a4ae67a26b88d90665f5143b503998374a0175f2e2f6ac5bd25f21fc62ff01bf96ea49a15b1585441c91bde7e634ced455dec7d3a6ba115ff3b12bcfe04cbecf6af5afb4fc5b98392d2fc2deb203ad0c5f3b7ffc2fe116d7a4c40487f5d56421b031f17ef5592e9d2f052fbd8e62dc004b9caf7ced9204fc49cb2ba40ab94bffebc09851f785230ff2137e992a04aa7f63305c9d2e3b6f07f94a09b17fb79eb6cbd5f76f84ec738e80b75a229df1215931a14bc22f14a7d85a2f2f9ba9e7cb01be437e6117178c7640b766eed27820449bb3a477d3bc9f75acd433701d2a01d2dd0d7d29deaafce62cc16b83e0fa72229c045af4f16d9cf33e1e3e8a137e7da245a9906e0968fde8037a6c6a6de0ca425650c10ce8311fc1290f02ad70256bec8a70501866c727c1abd2ec9e38bee97010e2f6727888ec24d15235f58458f6436545aee826d06569824efc250d8064406cbeb735e6e2a5c0c04b2fb267952091813646018655d1bc38828d4ff512fa4c127245ee0436ad4f39272cf0024eacfeefc8adabb6947d7ca619a5fdf16e45cce1bab1b93881eba5827332e4f6449871e1281e94b61e8e4a0827314b502af91e4dc372034dc4a76beabd158014fe0909c4218308ec23258a09de7c2761bb90c07736faa900e44c1d46252fdeaa69dff7de1b235950537043d28b8f55faac068f7cc307716524d691d11d2f9243ed14b10686e7ad6507d91ba599f76e4b82b8b7b3aeb91355cae7595b913b280b6e2063544a350d855e06732d72bbeae44c4aeb3c2617b137ae71ec54f0073b0aea31ddc7f9cc230542410be56f25ff12bfad25a02753a051bdbb793fc1388ef1a294b525afb5d1f00909d9b267ae4ccc7e62cb6418edb56a9752619ada13cf24c7c383c68f33daf425bca04b9b991cafaa21932e248ede5f0ba666ddc92dfc894627ef08847726002572e51a952e48c875a9f3daf5529b80734622d2ff33c1361dbcf2db76b5a76408eaf5060428763de6150cb883997488cd2936b1f92ff9bbe5eea42c55f090e11affba3154c01261267b65ab13ceebd58ed5676ccd1340e922109ec3a5b7dc115511486c308db7de52b192f642f2e9f5344b66e57d0f380b2bd318c435a9c095db5133ed53c9dde09879743b172c3f3464a0526cc3083d04c8da3b467eb9197d696e3888e3cd59bdd01261b529eb7da5ac57143c130195f5b5d7c0cf75c2bdbfc9e98b56b449f8e9b4eec249a140a3bf22498bcba318c35a7394acd36f79d67942147df440b6c249ad6f4a650ad12479fc7dbd474b34415fe6ed5b1ae62116f2d0738e84cb74814365c2eaae75d75c1e97ed02e65012e3f78505e42017786cbbaa290db6104da5df3ce5eb84dbe2f4cf2a0a36e5391814e4d05b2fb4a876636367e9c44fc975e77e4f56e62edc1cecfe6bc75550c00f2a0d3629f3b2314cb8dbda80b03437fd88eff616a3a4a03000ba3936b2d33229b09823de81235d67577d92e61a9bc8f31f5656db69ff54c97c1e410d2281c40e8ea799a3a8fe19383398856c12fa95693d545567d3df118494c401c9d77236045bd62a651af5c4842f0b119996312a2b555cb4799bec2890ba4a4624ce3eaf38e988510a7f5bf9573611d0b9b88b8606cfd1b7e57355a28110f9f0a8b46a2f235f9470ba45f0063556084640f79d837c8071b34408ff7bb78d08ffcac535fa3de0685b5daa94a89ced1a1c6d32ab63bbbb1b50c1d6033e9e4c377d5eccd0533b53587f4728291d7bba94c9dc15c64df469f29bdfd5cc621fee43bb33b5969111de0aa7cd65dcf0c927edf37ca242bc64f810c9c7c6ea9652f876d05219ea660688d07d7e0e85625e47afd3652b4bf13d5e28dc94076b12f7640edfe303bd5a1dd07ddfcd29675632f62843b8268a3291f38c53fb115f5325f8e71b5f728319296d7181d8dd5df7524b1d0908edc699c48fa5868f5e7f6015f649b6d82998d43e061cf94fbfc3562c72dccfd085fd0842be56d141c081d4431d7efe66c3c9581e68f74c739bec99b003890c5f9e37497353d6937da0a355128c4ba0d657177518157e811454c0000a340b6ea94d644fc967a3b1934806e4ffb2b6c44fd68f0d0771854cd92db6a4cacb4a51cba72e98077b8de6867885151c9d5a6875764e230f2ca135d546f10057a2f243c0227c92ce23cb1c219cc8225ced38a987ec5c80148bb8fcd36c5fe12d1b097cca1c669739ed04641d14a5614852b71e600f39f100b48bc384befa6e9184eda4515f30e48f2d8e7da182994a2d8039a5f167743e3a83ae225772c31dc15ed64829b285dce99d29d8ffc28b77bae6a5db570b676327eaca9a081330b3d950b25cf9b0199dbd9d734407a7b6788eb1c7b190b326161f86133f7061573dab25df10bdb9ad0327bcd8e6376d60b011265bb48603bf2730e8737fd3675bb1fbacf0971ac3818eaef2e48d264d80b74033992b6dccaa3309b771cacbabc31f1b5df9c5ca982c8cdc813956b9c7afa7651e781f31161bb61e68141b0bc9bb46de37e468170cdeaea20c730b81eaef4a7d51506a6e8d40e6355aeb1e3b51d43cd39ff4eeba80cdb70a6a0d85dbadcd64a80898df76cedb8506902f2e63206225147df250d4226fd9cf1c6f5cea87167a81630d7234e4048216a7758da572a26702d5083b1e1906281bf92eedfc2f988ec2828dd7079430bd2f0111edcac208beea9c4bb01ad70123a7d335d191fabfd6c637e9899fbf50262addadf5a3cf4a138954738b456bb9c8605625b2aae94128991e91c0e62828ad204e522ba6a55ab1c3064d56514da5c46943a1d8421fac70d584daea333df201203be96e5b6effe1725e541deedbedfb1c99d73d857321b5c79bc7e5078d34763f5ecdf30bf8ce3509263ff8516cc11b0d4e7892442f6756e23f05a98759de5209589c86c716d28ec30376b1aa2603a35615d9f712aa112c19b4364cdd55947c12dd8704ed1e6d804a6f3947fedb1c22e6b5d9ec4182ce82248484f1c077d8fb18ea2633caeff87daa396089a27078c0670fcdaba1d61091e2565950799be18b329f57e4cf4c80010c48ce4d970bffb525ba672ef04cf1ccd1feaf4d91a7f4b60011945364a4888d78e0538abfc2c85c4079e0895718a5802098fbca9570219d11d695164cc61e58502be4f8f3a3a2f4b5765d870ff98bb237b86180fef9029d0093b7fe806893d1a25440698b239a1ff3db8f7c8a687739f3d180d81fbe75bb323585016d69348c8d20c9704547fedfbcb90f82bfd3337e280f81fa7cefc7fc2b71645de84bb5e75a52227b773ed01aa743182c1716e3b8fb62984300696c2c29a14cdeb8033ff2952105b54d50e4a2bfb590f59a794dc6e3cd293106f806e838451e7a45d953a214dc1d630563b895880aabfce34ce9261c87a6be925eb37316382a0f8c5440254be1aad69e7685730d42bbcd0576627c38848bdc92b16ef61366fb6632202f483f25032043f06f85009b4e13015865a387bfdd7c6aa4436b32ab04041848b968bb97e0d19a0c0e0c0899c1f463547e9250ba8d13109dc52e216b614f1193a6f44d58959e8b4acd4511d9d1c4adedb9aa9c3cdaaa5e65c6641b8194bca4a574ea98a907b79f0e10b1d729c9a1e42a87b8df7abf1d73cb835a3df2469b47696d9512576d9aef15ae943e48cb86c535bee7370589f9b288f1062f9c16fd7d5e9033b8a1efe20773a313c68ee4219f9d449fbd9e67f55d276f6a21c305edddfeefae11444635ef64f2a9e69e6de505fa9dc82822033265a1d86a1a301695d0ae9310d9e29b253f31af878631b6e750e150004b1aea96be32d6a4380e2952c0b67ccdbc2d363cac6cb66bde674cc0e413a195459503b78c9ab8a606da64a170c63479a96dd3051b1e97153be047747cb20fccaeac1512d28b8126136b95a4379f59382bef28ff51f6b04ff085ee6762b7772b81dcdb4efcb95b52175d52325c8e954c6864096e93b87ea0cbf575ac028eccc698a5fc6f4a07ceea0ade09f88abd662057f7fb3a520d2c473cccb2a7047fc28f797936c16d993f2011e9524fca9d46832acdcd6ef1a8027bbe285d916bd31617d59ed53c7e1289661106b25a937c68e755d23cfa9491056c2c3e12c503c4d2c72ed942e55f2359243c85569cc240b7d9a81eb5e7836431cb4b5c5e26fd627e24a9e7d1487c4e7f64232ca1f23048158092016ef5e7647e0ae4d313a6422be0e998446fbf79cb7d70b33962e0f6c2ceea814a8ce8d8ea72186fec4f5dcc63ef82a577f5a8e84579623f6993b5b6c76e4a5390b5557d7b8a085229827c08f2ee968dfa49997e59ed068d4118b0339605305b8a2b1ba3a54cd9eb915acf75eeb0748da73e2b20c81de83de1b74181574e8648315a12a39276361845bbeaad5604229d7f4e3a08af4a9eaf93fbb8db90b0015992587e6520cc603516374320ab70b61c9ac5c1e8e81c97b1fe15916f7791b7f657c27f5fa5c0db41245a9615a4b50d3802fbdad494b6eb22abc3bcf030622c7e4067dc6941b961ec685fb554acde232580c42608d4eb9c0dd0659567348696cb99e33efc5063455cc0820f886e4b3b2cce680c0f77dd77098eb87d7f35474f1e5f2dba3329dccba450c6f97f5d2298056afd4218aca1ee835621896838a791e2e04d17f0b4f7f3a3f30a0fb717e054bf11f775e20d7a5aac4841c31cb8d3570855504be775c4582963bb2538189e204a8bfdfa59955a2bac2705dd78a6f8c9f3259326febcf70a337166ee934856fc8234a2d61b044578b940179b590b40363159538658a9b39ce9d39d51aface156c9149f47912f50dfb5b8e241c2e60f78bfa45e2996a981bf51afe1989bb2ad6a8cd6ecab3424893eb760aa4ac1ad724b15d7859915bcd9d625624570e454b5e1eb69b441c729d42a2755bfc3fc8d7e7a5c38fc07b576ae9904eae0104652be03e99b8e98183b175596708f40aced3e814c940b590a26d34206808438a41d169a6a33b6b47f3d3c4a2c8ae643d8cf184833cd6c9a0de44070557be274e0cbe89991ee768df8a98bcf180159187fafc44d9a91e3fabbd81e2d66ea303b963059c215562aba48b8e76c215b06757bbe70d2452fc57d003f4a5081fc4c4849bccf0ac8cf9504f34fb2c383eebad5a6d105c16d83a902b1adbd347dff749bec214d209cfddc171b6ee5ea3b201c07c622d2b7e4ecaf989ff8ecf7b4682b83b0c84d0766547e77ea1d63cb8e45e86b6df0e912b4ac70c3e15784484d714f8873e91ef724ec0b5396e2700d35607cd73847cb966bd95a18ce5b3d32456a76cb1a96172034f8391f2b63c305132e745cbe5426c5c31a30e77108459507674ac0aa22c2e6df15f11285529ddf11e1865d4b3616fe0f04aa186eb8d9cfcce8440fac9fee9cf0fc1872f4a0ad28236f3e66d8123d80d08590d5ad6d31dd69ea10b762b3698ece119d190d53f004b65c947d46b3967db026d76b9460bd78f67077eb78e8828f2d6a2fe2452ccbe006f201977461bf38e3b136e04293b65b07bb1f08070bbd8cf284b76ee4b21faa01c880ee6376b9746f03c9ad034fefce1fd5df27ad9073299966771be915e68636"
let t = readZebraTransaction h
it "Sapling component is read" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let sb = zt_sBundle t'
fromRawSBundle sb `shouldNotBe` Nothing
describe "Raw transaction with Orchard" $ do
let h =
hexString
"050000800a27a726b4d0d6c200000000ff8e210000000001146cc65bd6d252d09b8eb0a8ab0aab6d7a798325aefc1d3032fc6d31373a85a25a3a16b447a698f720ade1bc290a74d85574b5b20515391035a67f8d5883dc65ea3ba4a17b009d6f325d41072b3ce240270959a7ffd040e5f16c697d8148973c62ffe037fc83aded21e4c91722b52520a2395c23e9c1a896f4b0f12d32ae8e31833d9d95adae40f6ecf7aff52af184efd390a4c1aa76b5fb1cab6003b1a8a004016f385926661f56d38273ec2c3df7775210310a65fff5fa9ac5509f0784eefea28bdcc67b0ff69eef930335f3b9768529e2bfe733024492101f642f989de8cbf04dd66638e9317780bce47085079675b772664c8007e96597dba83ea9af22ddf07ff1c212983d4a902914431245357527294e69ea5616e720ef1e9215bbfa33ba108b8d07efff2bad1850525d7725c681761c9b8c844a5548afabf176863de7b4cde3901defc3e83d31086d3c6e6af9a5fcc3cfb38b52ac7de84f91df5e0587f7603773401a62eeef10cd3ccf4d927ef42402c32f32280abbeaac33e73ceda52089820a186e9a1adfea81453998c6bbaa0deb41bc4f94586bfee80bad25fc71abe7c6dd44bcb1a6929a0112c7e4f8fcadb9745bde9422b954f72954c4d22db48719de61f383d620935b647337f73d119d79fd208e1d5a92f0855447df5782cd4764ba91efa65d9e4ebaa34e2eccb7aac93a5b0efe0c7664f3cd9384b3ff706ad3019c907cdcfa084351c9f6a0bfa8c78c91272ca66ac86dd6e1d0d6ba9704ea7dc54f71a053dce91f844c1ca62b5ddfe6b53834f4a816b1b01460810d9b87517659f4915adf4b84783a60ecf3bd71569259f1ff90a91a0b314bd4c77976d7893bf42e5d6ad0f8df95eb6d6c69d41490be8e39b2452df3bebfc297d5b0fc97f081890390fb0727a96898585f0120a7da9a798f2032590553f724d8756c67c5b0d1c0d23301c4ed60fa283994fd712aab17ca6360256fd5aef0ebc48f0256e3eda5894b53981d0d46768aefdc85b48c1525b7f134dce5d4ec2d76c03c821513f1652d9671219d744bdce5e69b9a74ca0c7c837668f0d8ffffffffffff9534b3d594e1609b3bace18608750b35a066c57f85e291d194400cb351430bbbe212abba32be071e747b7310863bd5fd989855a6567a351b288144b6e9f838c6a517db94673246ef0010b65f9c0be8aca654f6f57b83d893663cfd389ab96ce50e8077fe588c16b1b5989c6cc262e6658efb9b88ac800e49e9e5999e2651b8fff28fa77071d63790df155ed8344e2581ac5205b31d4f17bd748fcf60e35a9d6048d23c94c7aca8d4e541fda497aa268df9c173af5877a5da56d8fa2a42166900c734b62e56792f6c8bed48e4f108a817e83d64d6a59e38cfdb55c0f8a89bc7507c89326266f7ac03a3941f448cb879bd792bb116d0be8876c0856a76ddec0f0c02e16f0338626013ee5f6037fc6a3c69fa291204039d04d17c11295ee3024aea8f5d381e9b7eb3f938b6f9182bf4f889f1e53e30f998b1cdd23f45cfaa20aaef058248cc2e1c487fcdf54a4bc22a68a17cb6fa7b2fbf333b99feb84643d321398b675634929602126b2fb40171e514769bf82f18c267ce9cda0c24300caa9a5a361144d3b7b9ab2243ee9811d9b2e72c8bb1d145cdfcf6b29994a969b41c47208f5dba8d6d871e490e9b970afec4d8bca40ba51825cdc78cc7cde6b6f235a4105b1d1b5e2765efd753095ce770f070b02cce3316721b9345680c146c2f428c0bbca90d5a8cd0a1c4c31cbfa8ec165ea9f9c71d2d05e3cf8bae5e779786f179c45a3cd8087d820cae812aded04f8acda9068af80ea834f79f1bd03bfd66f8a19074649a85ce877df1a621a867debb423ec0d19015b326fcf6f143aba34029c1da2fc7b099378a366c38c9609ef6a9d9e175e21b0c1ab94a84e28ee7f1a00e39cb6fb59f44e4567e9f85f8f98158263c52ec433c042397c784edb07c28d2bca036f59090e819157375d610acb1993a4107b48da13a371f5383429baee209b2c0cc150fcef79a042749668ba1f89ad24a8c746142191ed0e8fd63624a331d98d50daa84ccf9043076947cf5115b9f8787acd36000c5e72c8d783b29bb28a3e46036d0a592ce8a158ee5a7ac210be72d3a6185c13645d96a8446021b64043ab8b589a20091c152e7d5a993ba94770eea988e289e1536d0d81dbc7046ca9c6d918446bf970894f073c920006681ccf6d1a3f138519c68eba0296069e42dc60f2bcd0f17c400efe4f4e87de8606606dc4fdf31494df4d454d14a440b1d9db4265c7aa9bc8683c68cb149f2cc826427575e2af82e842199a9cb9fdc7243b3bc12f1a71c37eac5cf88ba830cb95728897fa4c177a290d6b2b3814173262da14db9b4ef39fc54f888a6ffef4221ae672fb03bc78ebef479360a682ddb12ea0369a428a6c2960ff8327e9a2f5e5d98ce1eae748db8f6a4631c789b4d751d6b99c97c149a813998d44a7b57ba06c8bcb8a6c73c6388cdcfeb1346cec8fee7bdebf2a2388d9722183eb2d2e0e183cdd092152ef640880f4514f3c5e836cc3a8249413500630aa8da85f9e3cd92bdadbb69a2bab8d71f0b3ec5832a7ddbddd67b34c33b2e12a0c8468e852e4a8f7df45657e9632088aa7c6c5048a2686019cfec33b27fc88e23759938dd55a5dff589c1c21a37da617609e9d8be37dbf9bd6e84ee160fe10268171d969e4611afe9d3482ed4b132dcdd11ee516f36d512a333da20266fd984caebf4937fdfd18ed07b4a45771cf5c8c16c6b258b289a07d136a22acc766011f366c420bafb8fc1a10e42219bede5a3d1166c525491ab60bbd1f973fd3fb2e94cea888e24d5fb0adce51faeda75d62de70094d4b36d38d03cd824d284fad577c3ead4d98bcc8ceccd18174a889b22380bfcc12656e764ea0b8fe1409971283008ed02cbef89d6f544c62c3b001bfe96723fda9190deecba534d69cfa358036fdaf16127b89f925c52d4e750919ffb7182b6a8ad13d0a8e00e0b906978dd24ee11869c1a63837a80e46e1216e2e273aba07aa5b0d97558db0ba7f9ac4c89403c65f1719394e479311f5cf84746e6be6f1abcac03194aa8bf1735811198b5df90dd6cac345779c185c24beda0101b932048dc4144af664a63acc0c395052882ee1f18bd0ddf13bb583861923bc00ed5ae815b964698ca097eda1c4281e039139fa3091890244f926cc4ab773ca8a35d5263d3bb48fd6ac53a4bb4d7d60b36446dbc714c35b5e13a17c5b0c70f67207839d1f7404604aff63b2fa83a4da7dac92aac96b3f250412f8d04a9e298004313b02edefd076c67d8a1316355777814e2e1ab03690e426b672d32ff65c03c592ecce6a70e34fea2e15b9a6b4fd092d027199caf27e84e25c09380b38a5eb8985355b3259aa1d94be74269b84f953053b02ba3be9df872ae5fb2d893188575bdfe222ba267b5461a0d0be274a7d9e6ee51490d98e4cd97978804c4f0f8e9f4908fd8c102b01080f5a02b7578591e95d60f3f56d8e48514b1ce7ea6894f55a32c8ac8564985d18c6b82f8dcde5b315624e9321bdd49dd350c87907cc373c0238a79321e6250e38a0ceb2c060ecee6708c11cb30a49687da9923bcdf011f9aca27e6eb5a8477a2bae2dcff9884cc2349b51a66b5179ed2d8f69e4bbba74c694194e83d04a8566228227eb732a95180c6788483d1f259d52c52fe43357656d50a1cf2902c3124d60d15fc85f0447a1203f824c1106452cfec1c92b18de003f82a0000000000001cbd27436a221a53d08c4838831d1bc60ff7e93df41a51412ef6096eec98bb28fd601c53a5371b23a497062635b5cdde715c23840d37f1cf328f0a2ba96260357689ae3f84a80dbdca1520df68513be1285177d3c0da664c64944de78d8b8d5864f5ac15444cd3204adc4fe487503066c18fbbef8d0515248b0a97577f5aea1d255788ed4bb66d4d56303efe135063392c312b4671963daa20e0ade262984e11263a1588eba3cf829e6131ab506e6a850aacce603e8ecfd6e794c90a772603d80fd2aad6027b34854072a0d23079252adb1ba637bbc650ed4afd35d977e1498d998020bc1c814718b48ba7378a92c56827d3c2f20daa231fa51f0a9188520e2a11149e162489f0d6dbd27cf94fd5775311d3dfbcfeb431bafc3515bbb8c4ba4488c320dca0dfec548fe9f46d8810b3f6b16bb3e3eb0ea130747d3d127c5953ca8d561f8d425a35dc3f2cd831743139fbdcada42308b524313782e23b32d5d54a265eae408623e3b2779fe60e13cf47d54dfe520f9f4e57c68aed31f78629a9074d72ab87bea993a38f95ab40df3ef01735e7d44ad365a786e0d3032f1c1dc4e6839c974185dbe63f8725e79831ebe269f94c96705639ab38d5d0700da04c6a9f686e1ea13391885287ba43cf3ccef1c2227918f15ed55441c45adca84153530bbfea3cf37adbf84831a2bfcbf0ca4a4bbd90e623789fe993dc17503ec11b1ef3049f27b27ff778af364d634a46165cda1dd8241cb88740bce74a73e7e3d656df2dee05bb561a85e64671b191ec802c5bfaca49b8168e44271cf13df756395896ff41a99654f55b6951f20d04b2007938a420218db8e37445ef3267130e288e3270b13a92596a26043e1ae84f3934cdb13363bc2843f74a0f6608a36b52c985132aa427c56b7275a864b3c76502c37b8abb8d0286b3199c78492ba8103f5a23c6cdca2292c75d7d6d7080108850807f78af3dc7e418371c6b8951bd89b79fa586af4e16096b08ac1f4dc2b1e4feaa5c040bb002b57311523197b6e2bef5b79ac9c9b4a339be6f6bf7fbe9b5c93862c87be6647949c70bb2c7e268e2ab39cbadd69de628376b3af744eeabc85b599bbdd09defacefa443e05c9b5f259a7783743fecf1a749c57cacc85703269ed67db1d8d475f6fe25d66f84a77379411ba123d98fcb3ae4eec306489a08372893616a91268ea6bf34ddbf0fdef1360ab9e82f4ac80a24e41f439af06fadc223c61f445b7261eda5e1320e269d1277631ee2245cf930244bf8c04050c514e2d59035b80827586cbfeb7da7a59c1208aa86390b9dc7a9b6ef38879ba4deea5eef47c5c98d9167594cd730abdfaf082090efe759d1b13199d739c112ae324ba24b275bf1d89867b81f4580a7ea3a8d3d07b45e2de6c1c7099de3606873b13f3083ecd1e84456c9a1b1d358075c68b1a7cf0b1f26031a2909e226f5da7877d0085b879165ec4b1d9abb7b0732ab4a6f22d9a7bbd0d494ef3f9af4903dc733fe92c6b2f557d1406d223a93e8ad6e579ebcde9c39a5652ad31335df924e5b6a09a0191821b4a0c8f886e2d7860b75ae79ad9dfbebf3500c8b9762dcd131eb5c8b866b5efb4fbfdcc5e31605c2b7d2ff8db5198a6c41bcf880065ff232ff8f84ca3f8022d3428359dc9fb19f57a6ad3f3d174d8a348879a754b37095f01d9a7f6f873798b97dfc5d7c7eaf0383b3fdccdcc11b30dbb3a0fe3186a36c4ddc9674624e38a81cca60a9bbc1b124021b61a383b7547d6af187022c133ba9d6dadf711a3af3b0255b859b214ef6c5dec592248fc94339a64f19196ca0fdad80f7f8e3d78b1f783b1f038008d0d106bd86e23e33ae5728872d42a555bb36d0e3303f0b4ab41180f4251590ee3ee244b77191c31b9f3f990f71c6e237b9dbcf7ca21c9b4c2446b856c67861785bb9edb920b8f530a7a088313ef044419a879f26db137e1557d079315844f9f60bae03d8cffa7a28bd2857a001fd5d2d999fca95ff91df0e228567f6c9ff592b77b7ccdc93a951f7e34910361a8f4fb517e1c9fb956a3bb50ddf37ed37e8d26adfc0f71e059ba95ec77a1e34e1b3420c893f89b79fc72e3a1d864fd35526727f939badc29740f5ef9c0bb9a3f72e1e08b2ef2ab366f80d8e14e03e92162736e2ccb7cde82b2af08de8a6a81c03c63e2396974ec29fb122d818a2d2d5b29d11b704d3ac3b39431099b7b2f6aae04d28a2182b55380503127e4986ee9e8d5c0c2058e09e4592d08d013a4f088e45403720160622236bd56fbd9cc240efceb1b23c19ceafec49e9d5776ca9da5f7810ac979cf6ec5f678c09257abf79c9ab55dea00054e11b62c0a0ed4363d0a96a37ae1a323aa93bb1af253885afb684ed30caf5cf3b37afe6a6463a16f34cc28b4530c6bc6281f597bea476cd9a773205b96d47ed4b0bfeeee39b7ea44ff194911bcddbb161c2c0ce9488978b99e880d8e43624dbb4a567483ed293348d752634b2f46219575175e3c249b8e4e853142b66491aa1c142e7bb558955747cf2bd61ac802a2a4784d9fe080f771dd537d0ff928b3b04029d9ac03175c2d535ad7f3c123eff30c0437b32dd9fa31b2976e369b89b79a2a95e31aee15a462c5fe25fd937ce6b0795808d16163f2cea8f19b7c83913cb4a793576aa5c0ddfb6415326d8f2be0621017616c85ee46aa768b077dbec72311cb8a0f78ba0621a277a79af6607063839a52c6825a20e1c5818b24628862aff5fbbfe87311866b9956eeea7412ef69a3e4da84699b8d8b45c74ac96c3356989fd4962ba79fc26a92488304fd9f42486266b433eeb57368d26ae91ba4e7ed812581f790314cc7f44639aa7f6775618111369e8e2d68a6ab24388824bbbe3c3b0fbfb88635c1fc3216af1af40eba3555c0b0390a18ccba9e68afbcd21ecb212aaf82846f0a55793945902c48cac5d19332e23248a464529f4cca177137c508b6b13637e7df523254f24b8343d19164174202bcb00d5fe618b760c374f69b5065b1f9acf91af95abd7eb271586cae14fc835f633aacb4cd2ecd0f0ac08b688ef4d13b8a7b4c487ad46485bde0e340309672dc38af275e6ab525971409a39eff0ad134b1674db5d1f9725874e36d8730dc034b0a596c6e0a26e521c199d3e3f86815a64d148ffc394290b19f15390934b5d0da27dc8365360511628b93ebc375d2a531e4f4cfa031eeb501afe96d201c7b6078bbceb8e5d8615599f4c613bbb81a88f4eeaa57a9c008125073a30154044c422eaf2a32cecb15aab0774bb44e52b1792d154b8036ff9224af53e023fc011ab251d47d7d76e55c5015db1926d43c56d055feca1259de53ce98a2faecb5843ce17a3e83ccf678f5d13a8321f278a670c684e62b720e1eefd0abb2e9d0a3ddea81d5eb6e380a1c22af5587daac852e93a86f5e4293c18bb26b32035c7e5ca20cd2e3eabd3de0e55092a79a42c7ccc0aef033d6683043c2a29531a2ef1f503595c0e464ac042153c4685b062275f88bab93cc017f1ef9dc6f8aeab7b0b234f1c543420324512554f1786c82b37836238a4dfdf86f97d09ae466eb39d5f3ab159e2060be309d1284be133ab40abd61468c1706f7f9e57a7bc747479693a03edc8863dd196fd7cb2721260e42f4f606389c4972c74d357e7467b61cb7f455562015d29a59c7cafe0df03f26b77bca81c2bbac8cbccf8a65190b0c4e5ca832e82ce4e11044433aa397106cafc05634ad778270d20d8a13068586bc6b582ea24524fd921a5ee22dbae5296ee86d80f12b78bba26f8b42c1401b75d5cfcc4775c5cd1cb0a9248dabc8f82d216787b2a2780f7d13a5ee8c6ab56399a8dd5db3a152677d01eb8ee98d1927ca5069e0d1ba3907971a2199ba3634b48e570dd97d93729a6c43e4f359e2d89795218d52270a338a1f511b1f008cd04553c1a89caf987fd18c329be7ac2282084ef1789615d7eb7afd2261f606d3953b8863abe57796289e7761b01c3ca0faf2291287f9ad7027d7f0876b5f77d2a7f87dfa6ad4db905d4bcad042f403824aee3b4f8d7b5ab027fe1eda9d683db24f56f694b0b10ec72ba0df40bdd6e52b4a7b8d064ad46c7490c4705c14b06ef55222435d2d6316c7dfee83d225eaa431c11a4b85b0bafbe66fda1abadaec8eabcc2f8c688a7b9cb2942597f20cfafd7520892c535bbf6359a6989a84dd89989d95b8e5222c3aca2e8b0f8881d759e450466b75b5f36b7b723b0a212edf52abd591e7e545a3974b8b31b84b523af7b47e3804b5a268c86ae0bd7c80bc6b578b79f749eccdb4a00813925ef40259ac10bbd0fc4f2fc536c30f7c1efe68a52bee22f57021d23f445211b36fee6302202c9b62c6cf9064a2df424563f9f805e51c4092482253e4c258d53b80a2d26eec9fcdc104f7124d876a3ad573a7f419b0d67a41a34dac9d8f28cf9519b9c2677c9d1e720667d5fce26091d64c6df8b46c98b58017de0d055651e8caa3a230f57aef214ae2f27fa85400e34ce7087538fb6b854a6ad534780052210b8b8c90b4de4c2afbad9f58a71770ec186cfa44b61b53876bf904972078673845ec3181caaccf11f71a8e2a502deaf144f16df1df3bd81277dfbf6e1ae5a17363725ec31759b743066fbd4cafb5eda6e09418bc375f42e0dcbd4624dbe26c5fabb77152124f400e677004fcf3c862a9b5576140cbeb800fcde4409caae06286cb643842687bc6b89738374c7c759c911d7bbec8613f1fdd996d4b970fb6f2a9290a84c34d5fea6b8006357c8e6e9d4048d5a8ec476dab0b55e8358ecf7d27c31da86681f3fa74d072b1150223eaa21c4027378a99c8a2dbdc07d4062c401e92eddfff82841292bda2798c4e2ee9e09f618cc181c91a4dbefa44c410dff5cb705ee005e3a0470c13baddf9066109797a3b51e73a0ef229796d330aee0c0160529a4ca3b39e861ee5c4a0f78619007ecdf32266c7c42f0c972b91cccc45f6f8688b2692f298721cb5da39cbca9a5adef6969a2592ba421680241f8a5384bb92e70acf79c2f41d1171dfc6e1939887b9c8ea94429bcbf3532919fdcfcd0f443d3c95515b41e3f9c84bdcd3de1fd481f98482f667f2d017e3579208341e9a225f85516c8ca133cbada77598b6f596e6151eca377fcc8029cc99a879b26d975684173c0874509117ece4136bd2d69848f858c05e8ba3421499d7fb5e3e7645fc135117d8fdd1dc46bebaedfbad4dc7cc23fad6e696fe349712cf7579b4e63b38cc7d02a4c6a33ee4117d7ccdb86ea02cd791756b2a3c516d59d39ed83a8c328823f1934731820c187624219b487ca86edcc2f61a064e4e8d17f58f4a71462f3f0cfef6be95c0eb3737616de5954096d761a51534b36d798c651541acfe2e5bed6e58c45c46e014923a342409d49e782054f2a4877332e0adb0663dce0e84df0ff0d71c4c5ef18d9cacc8b8d47c78d53fc7649bab719334601c79a345d2101e65d4c3f1616741b24fdd9f7d6569fb8de67799648b323856c2ca96ac91823aa12249934c7050505258c43763ac9b174d55fee71c7817d65056c30d7853500656040621eff9f291e9db198772451cd3d58bc9421f6191a863777edf49125543b22ba127ea3063e1989bc4635d50bb22270948ba594b2066d91e1589aa0c1f476af8b170820ae0d2409df83819579621161ba55cc8d020e1ea68cedbccc9a737746d824b06e12cd2e3730260fdc52a68b6e142bdb997d8f93f25d241360285e7372939828fd54c015ba90bb4d553558ffa2558a43098644357860785addc455bf1ef4ffa5aebb8f002eb0f6a8ddd6f45edcc7cfe88d7ae7141b9113a4ab851b5fa8ff39c7024d2b3202841266f256b1f4ce4e4ea8b83d0067555ddd56fb1f8ed6cb5ce7340cdc403fac1dd3b48c373629202c70f1f95c0001b4f94a9b4bad9e1c64429b541ff57a2f2ea0c1fb2af8f0e490fac415cc3bcdd5739f2069e2bfb873206b8c8811619f1db39386cd2dd748c6301239b4e873f3d4618a43c52ef7d8abc0ca127881dc8809dc20a8730718c64c5a132cf638ec1e3b3cedaae55763637b4c6341baeff17e14c1e3c34e5d23befdddec0c7e25f0ebfa90a0bca07a0f5446507b7fb9cf91cb3cb8d24a111f46e7dbe3ea62b384eebecaf8de49b15b17f9cf151c4c3d33220b3adcf9f87222813ff2125120e77c638ab19c1daf9631d53b3efee67c3c40c9c23495848d4c5ecafc0998bf2704bb78ade666f414e9c6bb100b90451d93397d062b741a3ef20e7e6ebebab8238e2adf415775179c866eb4a2f628924674b20f711d2515d3b024e7e0fd3af6e116cfb0030709fb9bed4a441646c03dfa0b11cb460415ccb3c0999b738a0b07d55be741f3d2646a3d9e22bbfeb46b87acb6fcc2d83cdf44b656d2269a0734259a738d83a7429bfc99da7238b9497fbb10e0f623dc1f793e3be2326a7e55677e20ebb152f71794fd70d7698d0cff1334341b3a8eee38d66a021cae6f8dfe67913fdeb8d46efd9b77f28950aee7ce112f8f0dfc04132a99d95dfa923d391e06a578056ec8d15ddfb0fe8cd2790ed55f8254c6351d471c7273c2e61c2694bf6b0a1173cdc2d422246ca5cc51a9b8669dc331fe33bfe330d5167a244cf041f8444775562eaa8ddb92e0623d4c689db0919ece87a7feeb1ddd2d034a0d884d6bc8b5fa5fc7df00cf667dfc82c9e98e9b57fbe07fed391729034c467b46d3d16ed72c8ded069a117ca2d28e71ef4c37f1b1939590af19433ff2a784ff38134aacc59719673c53ed047d79e0c36f3acc03f64458826107853ff8d02de9362ffada79feabc597d019de6afb6577dbbefcaeaee801a49b1718151690b43a4d70b2fcb73ece3e3ea5fe2a9616cc6a9a494b8c6c7121497688652eb854e7bae19af3e5ae1a8df75138ce18621d11f15054ac8971f6ac084bd90e3909b46db321916c9e3c8950f3cc8e0a10dd255bd6b359c73dfde7de6e01538d03609253968177af14d327337bc1bc4e866c75ab5b69125f9e9a758f061418110a615b97f733f899382e2a14fa0ff0d1bda3b7449e1f54362697aef064fc9dd50692ad2b9ec286a1b7c1f845b09f4b1ccd46142f9e272d018eabf76c23c26b1831762a8a2c9152ef0cc843ebba20bf979e851e76141f41e817cca0ac60c1baf239a6c920a244b0be031a3b65d76eebae15bd9d49c53dd3e4303ca379f26840901f1da886d330d8ab4408a25eaa280ddb545b968b4aa59ca6f9cecbf82984264a3232b97498be0729f3d97dbd64823393b42794ec1676244212af382c37d53d66019a5db34c3d8ac156cac852ecb1c57f0f6057815460eb9f666c368acca6dfee0d2b7bdacd2148d74cea3bbac8b528f00809f403b1964b9d199d2db93363a53732efd97c66f28cb51442dbfb1c912dce078944e02889f9395ff2655e2d7118a11848e3ca64efb5eca119240b537b5d4548a744d96962e9b2a99af73bc4d753df16fe75a08005da7773b7bef68a44f56951a3a93995c5d0516a8b2c39abd2057b3d86ba7fe511dc33292596fcc4d3c67df88812e26d3628527752a81c8ce6eba711ae7ea8748229caa2580e18937f45cbfb0041f0a9254eade41c95ef4f390db6174e6b0ba89af344c215393b627348ebf00901e343c1599c832bd2cfb751750808719f18119ab3fee6baf5fd4b8bf7c92a271ff24576984610abaf1b2297c18a2784f57d7aad26f52c0d60fa6cd2484492a1524dd14924eebf53348ee2dca0643ef47381541b91d1843817067a94bb7c79e4e08c4d81c7266afefeda4a0db21b0db629cedf13b4f6ba11e6480116a7c9780009ee0e1e2bd543dbb68bea356a6010a1e3ded4229c4d8035420833fc83337dce4bc483eec76f479f33be2d6a8fb013813952eb66bc247f37f9def7e9bea0f277f380079aeb48c192b9c2c24f909435586bcb994ae6f75eea111c8842b855f90df21e956f839aef89bb1b96899e343cc66814ac08eed4defe5e2a62c54cf83a27f0499224e02bac652aebe6529143372c483edcdbfbdd5a02106fd3651875f23724d2154c0138ace745d038d6cf6b233ae2bbb8313c8ab59dc896844da5a6610845d9147e57518ada61b7debcd66111d609587632cccb936751d099639735f09f77b2b9aa1177e95ac0f8070bcbeb01bbfb3e7a638811d919016ed628865e2405aa17f5129ea43a836a76ec7cbd943e20b1e388d47db2df3394785e27dd7efbe350ccec6057af7483303a6a588d4005c05ff7f3c90dc5b9a0f95c3c541ea40615f6b0ed6cd3787e608e7612b52199062991a63511b3055dfab25b18ab55948d9d529aff91a0d8aa7705afc187e32d1107c07623a309390f32c62e1abb830bb090a7dc4c6817b554720fce354563662d78cc2bcd0483149b304ab86502f0ad36ba2c262fa60b1b84be2499e56fd72129c0fdf1961214df8dd6c9b831109fadc7464df090aac96d221ab387b6eee3080ea0d94e195ca795b5d80c42946c0c353e39075bb1aa4f3fac36a77445cd57b11b3c7ef0e89aca911be35fbb72e1e2fff84b7ece66bb8b3dea52b0aff473475d298f730e79ff77e955856817514908baaec1b53f39f9d5dcbb1cef29536a483a1845d8538e5ec43f634914bae4f2319579adcf3f0fbb151c80b54f02be54be612f9417f916579863dc16da5af07fab05db2094c77612b445b7b9784198abbfc8387486449969118d3a83142022017d69b939d30fd6b1c5ba360c0e37f264b5d604c7ebdaf327874b22d4092dcf35aad93f0f0b3037f90000d4a1892dab827d3c2f1ddede948d330706ef17bf442f8228c168401541ea89a837f81cfffde8480e1b6014f2f5d3132ee740e04622600ad93dba41e39267f17554b29102390f8196094de19e8d966d3c03a9e3348494b54045629224612734a1304ed76d17a465c305675bf99b7aff1950dd2012e6cbe8d15a9e35a7c16081bf647e2766c7b8e9d12ef8a2c94fd7531f6d1a7ac7ce6bac028917a2a71c8eded4f3ce3be8c7d98422094e88414338fc25839a582b5965c71179ce3ddf7a645bb163f3265f4bc4ebe91ff25d86520696d237741ba73084070e23494d6a890e1ae748091a6936a3137d16dd14571f86ee118bcb17c8ecd2ed20c25cbab79a78c53fba1d5db4fdb15e6a124bdb2d41fef080e7318f78c67451beff04b58a182f592fec3feab6011be4663dca8b62ad2a8e40c716b3c8a170e1574517c44e146109d223529b0de480aa168b5a4b0522cea03dd26a684b08c58aa2e1fe41f44ba8837737a74a4681b0508d9604aa7484c5ec68ccb369c056702b03f75722033597ce5b55e5a88f14e3ee23b1c96f5d38604128cf2e901a745996f4a29d412e5ce1b4ee82cf2f248aa80f8c8f2eb7b6951f1b0910857a17edc319bc76b1a59fe05f9394a5b16a91ef15afba9ee2e115c9c9afc51a8d7c4a61cb335724855007f564fec341342d53717023118917304c2dfcf0bd79e456c94718b8388767564d6055aad1d505f36506fd6e8cb1a10f6fe8a67e772030dab78b43ec15fae0322fe991944be86cd0bd78ba00110474dec84dd9597a602c4a6c97a27f13159ff1e32fa63e28f63278b09c9d051cabf8bb49f2780d4c33ca061ff16c6288ff29d46a22e31c719dfaf6087590ea8197c066f9057e0c24b40f5eb205b730985daaf7e7d85fecd7449677c0ec1abd7111d795de6de4a6ab30cf266987dd55569f2a705ae16f51de738fcc4cc218f2cb9dab8af509ba26ed93ddc7a73028d9187527fa6d554b491faade05852eb50847affb81b58d2501989d5b4a9de4237b33bea154c4d016b08f3e1c39ee61382fb500be2123ccfcd30e37c88979a19d636c9dcf5fcec822e5558cb2939e40e1af039aa942b503c9fce95f8ff51a2977f58e4d5032f9099fb4783b7764dd1299a1a89f21c1f5e38038f219e9a4e9d6c6edaef70132ee1f2a580227b256c9139d339183e278277a9dd260bf7d8a9853c4185f9b6509ab9b67a3563ed0b9afca6f2db28258025d8c2036cc4cd9fb4f7007dca96597c26476def29191cb0c77c44df80994ad3c79833820ce2f1108d17a083fa857397d2839c37899ce2e634578b3eee0135f66cfe1aeb7bcb8732fedba16093a6e666321236f56e285058d00c1bba2db3263844e4b0ea013e7a8d370ecae9ac7a9620eaaa244b25be1eb6e71368f550736d3ca13747edfb6254c9a373aadcc30a148907d39e81bdda8a53e8644b39a1625de851fc5d759686c63c76bb162b244415175eb0ccaf3a63d4ad910369e30313bdeb175ace63ad33e400e642dbdcb472a336d82d2f818e6888e5aef472d03fd298a5d14b8da432b72e6c5f5694887a7e4e165bc41055379bd204298d65796d5351a9121f949904bd6c87510649127ef5797d432c6c759939c99026d57076db225d7d0df01ab9b9e3e1221e7e323ff752eb7988cd2b2bbfc8ae7fdd23c3f7cfd2ae2679014e986e3c01b6c66a67fbf69483ad3e89c1f2dd0867f411151e048d81212c0b5e2fbba9d317f283f97c9576180121f41856355b906a22c69869bc27729db5974c5b1257b35d186dcb4e9f7bc51dc331c0ff9baed5955e5052a6ac7743b"
let t = readZebraTransaction h
it "Sapling component is read" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let sb = zt_sBundle t'
fromRawSBundle sb `shouldNotBe` Nothing
it "Orchard component is read" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let ob = zt_oBundle t'
fromRawOBundle ob `shouldNotBe` Nothing
it "Orchard CMX is present" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let ob = zt_oBundle t'
case fromRawOBundle ob of
Nothing -> assertFailure "Couldn't open the orchard bundle"
Just s -> toBytes (cmx (head (obActions s))) `shouldNotBe` ""
describe "Raw transaction with Transparent inputs" $ do
let h =
hexString
"0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000"
let t = readZebraTransaction h
it "TxIn parsing" $ do
case t of
Nothing -> assertFailure "Couldn't decode"
Just t' -> do
let tb = zt_tBundle t'
show tb `shouldNotBe` ""
{-
-describe "Sapling commitment trees" $ do
- let tree =
- SaplingCommitmentTree $
- hexString
- "01916df07670600aefa3b412a120d6b8d9a3d2ff9466a7ec770cd52d34ddb42313001000013c60b031a5e44650059fcc7101a3f551b807ab8b3a116a5a9c7fa0f3babbe735017c0d36686294ff19d59e58b6a2ac6a7ad607a804bc202c84012d8e94f233970c0128dbde5180af5304d8577376d78297130b615a327974c10881f6d876869aea05011b80b4ca60f74dfe33c78b062df73c84b8b44dab4604db16f5b61eea40134373010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
- let cmu1 =
- hexString
- "45e47c5df6f5c5e48aa3526e977b2d1b57eda57214e36f06128008cb17b0125f"
- let cmu2 =
- hexString
- "426ef44b3b22e0eeda7e4d2b62bac63966572b224e50f97ee56c9490cde4910d"
- let tree2 =
- hexString
- "01a47029e9b43722c57143a5d07681bff3e2315c9a28ad49d69e7c1f2f6e81ac160010000000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
- it "Commitment tree is updated correctly" $ do
- let t1 = updateSaplingCommitmentTree tree cmu1
- t1 `shouldNotBe` Nothing
- it "Incremental witness is generated" $ do
- let t1 = updateSaplingCommitmentTree tree cmu1
- case t1 of
- Nothing -> assertFailure "Failed to append node to tree"
- Just t -> getSaplingWitness t `shouldNotBe` Nothing
- it "Position of note is obtained" $ do
- let p =
- getSaplingNotePosition <$>
- (getSaplingWitness =<< updateSaplingCommitmentTree tree cmu1)
- p `shouldBe` Just 129405
-}
{- describe "Orchard commitment trees" $ do
let tree =
OrchardCommitmentTree $
hexString
"01d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d01bb83047c72eb4f71813d00dee37082169546df2d7097bf7fd187ef6a93063b281f015e710ed46b53b48b12733652e150f9dcbc7e7b571cf64f294cf903864c78882f01cac32bc901f501f714a028f7ebe44c1dd8b42661be1c96730066a6fa6ede653600000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
let cmx =
hexString
"d5c803729654208f33d33dc68ef539ea098abc5aec215ae67c4d8aa10a14e11d"
it "Commitment tree is updated correctly" $ do
let t1 = updateOrchardCommitmentTree tree cmx
t1 `shouldNotBe` Nothing
it "Incremental witness is generated" $ do
let t1 = updateOrchardCommitmentTree tree cmx
case t1 of
Nothing -> assertFailure "Failed to append node to tree"
Just t -> getOrchardWitness t `shouldNotBe` Nothing
it "Position of note is obtained" $ do
let p =
getOrchardNotePosition <$>
(getOrchardWitness =<< updateOrchardCommitmentTree tree cmx)
p `shouldBe` Just 39432 -}
describe "Extract Sapling Address - UA Valid" $ do
let sr =
getSaplingFromUA
"u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l"
it "Extract sapling address" $ do
case sr of
Nothing ->
assertFailure "UA invalid or does not contain a Sapling receiver"
Just t -> do
print t
t `shouldBe`
"zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60"
describe "Extract Sapling Address - UA Invalid" $ do
let sr =
getSaplingFromUA
"u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l"
it "Try to extract sapling address from invalid UA" $ do
sr `shouldBe` Nothing
describe "Decode a Transparent Address" $ do
let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e"
it "Try to decode a valid Transparent Address" $ do
print ta
ta `shouldNotBe` Nothing
it "Encode and decode should be the same" $ do
let ua =
"u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x"
case isValidUnifiedAddress ua of
Nothing -> assertFailure "Bad UA"
Just u -> do
let tAdd =
maybe
"No transparent"
(encodeTransparentReceiver (ua_net u)) $
t_rec u
(ta_receiver <$> decodeTransparentAddress (E.encodeUtf8 tAdd)) `shouldBe`
t_rec u
describe "Decode a Sapling Address (MainNet)" $ do
let sa =
decodeSaplingAddress
"zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60"
it "Try to decode a valid MainNet Sapling Address" $ do
case sa of
Nothing -> assertFailure "Failed to decode MainNet SaplingAddress"
Just s -- Sapling address decoded succesfully
-> do
let sh = encodeSaplingAddress (net_type s) (sa_receiver s)
case sh of
Nothing ->
assertFailure "Failed to encode MainNet SaplingAddress"
Just zsh -> do
print zsh
zsh `shouldBe`
"zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60"
describe "Decode a Sapling Address (TestNet)" $ do
let sa =
decodeSaplingAddress
"ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5"
it "Try to decode a valid TestNet Sapling Address " $ do
case sa of
Nothing -> assertFailure "Failed to decode TestNet SaplingAddress"
Just s -- Sapling address decoded succesfully
-> do
let sh = encodeSaplingAddress (net_type s) (sa_receiver s)
case sh of
Nothing ->
assertFailure "Failed to encode TestNet SaplingAddress"
Just zsh -> do
print zsh
zsh `shouldBe`
"ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5"
describe "Decode Sapling Output with spending key" $ do
let p =
Phrase
"cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest"
let seed = getWalletSeed p
let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0
let txHex =
hexString
"050000800a27a726b4d0d6c20000000005bd2500000001efdca98904a8b34a0fcfefc47240249a18a530289f2ff6a15bb9885abf6e541aa6c9a851b63df5cbd7473b5b5bc5efbc4a3130c15ce18b92b87b67de2cc72846babecb9030390f0ab02d54960636633695c0c1631f9e87625b92976b12492fcb02d260bd68e7c21cc8ffaad1c3ea5182dee524a09f7b5babcd7d6d85270d6d0d3d48aa620513b632cc17eca91e237b38b1212739e8df6ddcb85c2314cd76c52e26791e2d99fd7ea468b2fb4df3e9bce602daa6a6ad8a57fcee27ce5eee3da998902b5ecf4651adaed9bfd17d0d6de3f4d8c1d391f91c571a341f105e3afd913c25012c623f274406cccda52535a0b4ab883b763d1c7405e6aac941575a965ef0b9b0114588d9d7a2be82a3eb11c662c1fc85a31e8f07fa2c2a9dcb295d5530e893b2aa43c04a508eb182257c791aa4f75d6f5d77a8fbace89666ee80b572015ae4c33b8510e88a8a338da305237d1fc5d267a4ea6914d6f9cacd1a5e158a1acd2722284ff71fd870d92143872def753e2ecde9c04c3b80b513609d47a523243508548c8873426c52eaab76b9cf2b90e63fe9c5485eddfcacac65ca811c634df22a01610b22770ba3d294f566de7e0764641985b62ce2cd71735999111bb323b14d87e482201798672881da255062dc68f912343b911154f7c96442a01be25219f0315bc78c1c3693f80e9a9cd062eee2de8a37cf073f505d936f1be9ef9a78f95e8f70ef20e0e23c2c59c37cd94cfdc5c0d0d14849aea4aa6b6a7b33a0fc57a2a91a5950ba92fec1465db65b02e36fff82d2cacb4cc9038d1a8f30559a6211c52507f2dba723d54e08e82b127cd63fad97e44292e90aa87a1e798504d0f67f242561eecc3ab40911472d75457afecaf9a026e9e0ebcd07c7e78c0bd2963674fa51d6d2c157791c5d60574047a0a4ef496f1465117ed6b9408a107cd77daa0501bb49f548b97d6eb45ace8c0620b0361031f6031af5ff53015c6755fd3c76e9e45c8694d0f38722aa9bd31987fb503a0fef5f3f97033a19212e9d549b7f7de12c2dd3f1b5db353cb40c486a0ab00ecd80d1bc6ef96ba9855bbae57c6bb9965e1ac01d4d2cccd150a44a1e511bf8f9956e7472339306f7c14d90e61e4b01d6e3b98a6b5008ce102b4646d31fc11af90d4834dd8e0005fdc8ca2310fd6cdd4180def97074fedfe645e3c9f399ca9bd282b7743d943c95e4c76a7deae192be69b80f71f7f3dd9ff5199f7c82f743030ffdd69fe88463ef6efd62d329662ab48325d4d67bed52bcdf16d1991adf5fdeadb02e81bf627d5c58d013277f1ab04fec34985917a7495d4056d47986ebcdd18c08cd9f6ba3a1487f646c1a8d99714db89d3d7708cb3455d72943a609f3db3097b7ab295316c2bcf88c73ca159e995acddcc149fd186bd82cd34f02af8e4b68ad39db8621ae92db4d41f53d2567948a423ed2a7fe38b1ff77c0b1cf46d90077bd062b3c2edb9caab921b01c546866a376d29feeaee6ff8e7583c8b4a4f631c4cb339172e5b217ee75793f7608ea6d600c680f86ce63a89a5963ec5e20ade9464f3142c0100c8ce4426b34f887b68c9658cad2dbf8a1c5da4eb7b576bab32b5af129437cd34e3c8b8d17bece85cb661ed20224539ed3eb1d61eec727deb79585f8630a807acd44978dd6ec190312d1c8dacc4cf02783136d730ab0216f7105a9f11d090c5a6e69eb726ba5e785de2f99dc7a8051250544cc661b56913715fdd31cbb5fff3f0b5000a325db631ff60ad241051511dd32e322900df8f5707a59bbe8c1ac85f4be97cfe6700a9193458fa23a70b6639f0bdfd879493520d8e4e593002f5cd9d4bb60aad67610cfb03142a5fc9cefe08cf76a23706288779cefa08ba26e33b9ca58109e0e8abf150dceec584ceabe74e3c838f86a4c3920a37d8dac148317a3c3e132ecaa551ddb7085a8702dc970b5dee5264ee1bf8bc89e92912ff1f3b4e5d10627f3e55b7f6216c0249cb2aabd1267762cad1a88b6c81bc42ac47b6f53e5598ea1dce228165f0e748c0af90eaa421813dc38a521daf83e688c298ddc0a1adac1418f04a7739a6d4f7fdfae64320d8d8107f5bacb87596b7f777a85002d9dcf66c930bfa427a6508198d8cf625279b3c6b438e098f04453d9607bb83271b2ce1f11bcb4c59f0edc4135cf4d5bade66c422c654f9dff4fa720589975d05ae7b10b1d6d2ed72c26534be10a229605f4e946c21abdfd629b1ec18e5819a1be5c89fbf446c65b39fdb32ad5cd9079beee1b73bb3b3a37c3cc10270000000000007a2aed01922521f492d80b45e7d267745f3423c1760982418b9f69390660fb12a6ec696bed9fb392633c496f576a910a0c4d5e37ad76839d951d4895f76948c72fcd580590e014c9b0b3a091ec19ab40a5d59131024055636725f98c29303bab5718006e7154379227c58d18b04938b43c8fa4fe793db395754c3270d71f90dd0fd907c39c848446f99de6efefebe414f981c29f2eb448d1d15ff314ee0c763b66dbf6917e44e18310e0fa1340069512867157552c696a3f89c909668e0698e83825b1b0d73a38a2d99531a8c053a48acdcf9fa9bafcd8ce1f3c3b784bbe23775be45176a5b0a42978008ba5c60020714ba3c0174c9a068b45081267609f7ca31e32d7367cdb0ffb88c7cb762e55b48e4edc639da5ec739a1a5ae0d7b9a16000adb66352c377e3a316a18c66f3c994c0e62b81b3ebc485e4e3438426f1a32a3a875483def774eb45e13df7a14c9ac4f9ac22bcebf3515a6dafe9aad745451708180fadcce8870302e65d90954fd9cae32e3374c8734385c6b4bc3581d997e0f20e6db537a39c8f9b5e54e0a2bcc41e260387e4faabc2f09e7613e26c472d295ad56ee97affb96021f6e882eed25481628f5fab8bb4705cb7df163eb73ab45323dfaebf8e2108fb8c444833d640203455338459c1a68a0a8314f476ab6b230547ade1d2f2df907bb9fb0d2b6b0361e4d8a20118d8328274c71f84ee6aeb761c36fdae537fa747ab9a251fae846197d356b90913b00440e2c034c7cce498a01c5495dbda102d9151fb8f84bc55728a72d2fb5edf660d7df5a155212f6c10e12649028b172f8e4ad32e9f2898cf8e352c7898a2bdb30ff22e903371ec4fc47c49f1b56f0bae3975d80063f251203a5e15afa9af7bfd278e7af0cb5fc3d522639e8f4e3db7d8d5c483dfdc4207c36f0fa72df98f8970abeb8d872cd04a4113e6863db9dd9c72ce08ac8bb01da342fd43ac27a02a4b2db787efe8c98fc55a7730e43f98a85a13942a95545f630191fe4e879b515999c0a63f9eb318d6083de8bed00600"
let txHex2 =
hexString
"0400008085202f8900000000000045b52a00e80300000000000001638ca17ebf9c8fa40eb74be6cc0f702c8a009695b0ae1debc87f4623baa2fdb3f7d7639e3fc8c696cf5ee19d3efccf50e38546de089c4d95a4a3a4a5dd53ef4465bf1d0f111e8a87f1f499ba1d7acaa523f344046ed86c1a42d412fdfb6803cf8e155df11d1beec2d11aecc2cda622b4028db0691fd61d74969a96021bec1f45855f06c8e2c1e02aed41c4549f2e4227572f873847542e1e29077269460e2885cc0a1775378a20e7b1ef40d1e358020ea3c24991ea5bc150932d538b126ea8cdbe5fef9728efedf40912b75634c4ba8288a2ab6f09ad4b8081d395bb4dc49f221011891a8b4af3cc831bf5bdf013950d6a4c4442d314f62d18913a2e18f041cac9535ca8a973e38c3e9796f4eac80d4d88a39c701b17aaf452c19410fe45188d86d91fdee3799ba7bce793c3d0bfba9139c006eb4b279b48dbed04ff75564a068d4b022ac523d40f0a4c65d955873e7498df63bbd87b2518aaac084c8bd8d44d620a07d994ee09ae8e1792d47393b6b570ae1a9122dbf7bcc1f494120d20ca0c028e7443befa6ec5956c3b0c593c6065c7ee909f90de6188e38c24e57d8f0f4cc8fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858d322258e2bc7f6072ad014d8b249567b14276280bab8f2fe6b62ead86cda2dbf1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d86459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa08385f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62d283384bb097120b47b780b4a60b5c04d53e90c9ff2e781ac9b039384fbd48d1d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843e007aea2e07f40f139e24d0e497eac26727a2b9aad4f223887cd423b9fd6cf58353f0b011ea73b2a9843c25529df7b88f2357fba75353218012d1ba4b89b45d50564e9e7f16101c69c4232d2de683bd0960cc87a016dce7bb088720f8f6c092bdf2f2954e937168b780b2f8a7828d49f5b0d8809f25d74e6374065c4596c5a8ef2d2c4a18a0bbbb2ae9cb6d0f6332614270c4438ef07effd094df92b74cbb828305debaac629cb01a98e32d16e04d0b8c52c8a9a2c526481fd627d453e97bf33ea717775dc06abb09f0b95a435daf64e41d198ef6c686baffe891e9a1da8df943b12d1b5f192387b5b367db75593ace3399439621ec9d4195c188be12781cb165e559161381482bc513bb9a35d3746c30d88cc475642d63016c8a75347c1bb706bb3276218e6269e6e1a1c8912b077b5e82235ba837899c6c23bec2917457442f9f607d3d63b8c8ea5f065ca5e7a4ec037665a40003d9a0c95d97234ac9d50efa385cadfc79820a017abfd3f678ba5d7a8b890f5ca6c10ffbea407c6ac18638bde1806f739b7fd6ed49c665884f8cd4cdf77a9c02cad0ffa8971cdbe664ea7539521c87d9658808b112d1ea22b155208dfd693a48d5dd703279192ae5d9acab817c53193e17542db27d63764daccefb0be38b2bbdbd01345919e2f08811f99fb54a33033f6abc76c5ab4ebc9056f0e8d3005f80ffaf403ea744ff86a30fdc23f319972c4ccc81b3f6a23ad9fb00e480cd0b7d138e9db6e7cd25e424d29205782b06e4b0c5b932820ee2035fe657ec36489f60829d8c31dfe36accbc0fb78176508b6024b60909dba12747745d454c150f54945ef6a9a085e3a6b6539ace61f767e1e5fb1054355bf26622674d58d4b5037457138e50abfefe92b515c74c608fcd2a5af50eab0bc85d57ba3bda52fa00c9c15d44a24586b2cacc6be00209ae49202430645d4a5022703b1cace87829dc783d5092d97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e889200fa5344b9491cd5afeb2dcc03364b8a9380fa11a5c4ea09bb60d13aa217acc38fe3e83b5569bf75576dacb84cfc28cec75ee724fcb8b5d4fdb12948b1098ebc07"
let tree =
SaplingCommitmentTree $
hexString
"01fef9aa4cfdc8c26eb2693907e96eccaacd61ed04ed7860f1d83260e6e383b936001001bdd6575663c970df26d8eb84b0be6411f65337912c90b1a0a8ba2b9303326d350000000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
let sk =
SaplingSpendingKey
"\ETX\189\201\190\128\NUL\NUL\NUL\128$q\CAN+P\196\229VM\129:+\SI\171W\248)\216\196\215\141\229\239\141*Y\175}\253\232s\156(K\179\GS\139\232\246\ETB\181`l\226i\156\211#\tdNT\248\138i\220\136\209>U\150cl\f+\201i\132\250\220\208\137\178\196\238\141\\,\208\194*\245\145b\FS\244\164\230\194C`\ACK\140\220\194\ACK\190\224u\FS\167CK\181\DC2\r\US\152\237\238\137\209S\238\240D'\248 \255\164F\254)n\a\231r\128!\177\168\210\141\234\STX5\189\b=\191\ETX:\USgvB\155 \162\234\200\DC1$z\252\177C+\252"
let so1 =
ShieldedOutput
(hexString
"8e7443befa6ec5956c3b0c593c6065c7ee909f90de6188e38c24e57d8f0f4cc8")
(hexString
"fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")
(hexString
"d322258e2bc7f6072ad014d8b249567b14276280bab8f2fe6b62ead86cda2dbf")
(hexString
"1264a89d07964845acb2ac5f9187f01f3980f0edfcee5f4082ad30f0c9512766063a55974c876cfb1b2115cbd46d9d49a3929ba42f4f9ba5ab15ddb106376a24786ba6631860f1e04df72a8a9dde04fb9128d453436f34eb560f52629deded87d46f0a6355da1070f14a0ea10fcae274bbc1b3f190858eb32ef47cf4fbca4d86fabe737ca1291f40979b6cda84bb43231598363ac9fc3d9af0d74a0a0797eb0dacc6dbd2687f3c8cd92468d39915edf9e45161ed10d78e17b629c5d5c73a09ff737fb370ce80a710bc97c391936223e926563f0ba27ec983c09477e23f8c288e35c57da1ce05ab740dd2d7afc800257c17276e4b9970f08288623c506275e0b0c8d5f60aab32974c24164d1b547e756218acf68849eea30480f529c8c05e62a65c9d070285b46c4a477ca5df220c696364523cc5d6db9ca852b1eb036846b537f4b566913660ad8b83d9c72daeb45274a55032cab9a3e0b3a1e386829dd10c6ba01dbc393843d67c553d74be7969b1879bf0a2ce05f108159a0df47f279b490d5a45ca1d90ff1a61650c6ae87611746d35f8410b39f2301a1be54711d58243cbc3afa29df2a7be5565cff1cad0bd34c370c345b2f38ac7020ac79f9d6c9eeaf2c7165acade98faf18a4e1b55a4e2a18a6dd4790057008ad40c51b0967b87a8d54cde958801ef98ae4a6c9f0e94f5c02a498a9173780b5b0790c248241125fe762c4697a320f1d08d782029c4fcb34fc8580c8c9885ba8339c5693170c157d1ebc663cdccd0a4bf6e77bdc3026252696ae0317ff7a3d9c190476e4605128e50eb42d3d5d8")
(hexString
"6459d6fe8c22e5482822b61f0bcbfbc28374ed93ccbe97e3af400265f02ea525a8c4853dba3d667d7502e914f99ab5062f81fe1c699663694944c8a2fe4679a6866d1d3fc07acf31041881dad5faa083")
(hexString
"85f51c5880265fe6f6bbd7961cfb9c8ceb8d1f5e1a99f7bc606585136c18d2eb3b9222576614c7092ff47d32696b50faa67c0170a2b3212a32f7bd79daa69a84fe2ef3b62081d78418d0a5902dc65d9442f1824366d8906d514ff1db6bfcb39202257e9e523cea9c311c0d4c13efc6fe6bea7dbd0c1c9ad39594ebca64586701a2384d40e62198cf379ecf21ddd8522e98a447f1c0f79e03abd232da7c8b9c39260a21097eedff86744445c7785ba0251fea0572d5ec7f78134c75fd03fc3d62")
let so2 =
ShieldedOutput
(hexString
"d283384bb097120b47b780b4a60b5c04d53e90c9ff2e781ac9b039384fbd48d1")
(hexString
"d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")
(hexString
"e007aea2e07f40f139e24d0e497eac26727a2b9aad4f223887cd423b9fd6cf58")
(hexString
"353f0b011ea73b2a9843c25529df7b88f2357fba75353218012d1ba4b89b45d50564e9e7f16101c69c4232d2de683bd0960cc87a016dce7bb088720f8f6c092bdf2f2954e937168b780b2f8a7828d49f5b0d8809f25d74e6374065c4596c5a8ef2d2c4a18a0bbbb2ae9cb6d0f6332614270c4438ef07effd094df92b74cbb828305debaac629cb01a98e32d16e04d0b8c52c8a9a2c526481fd627d453e97bf33ea717775dc06abb09f0b95a435daf64e41d198ef6c686baffe891e9a1da8df943b12d1b5f192387b5b367db75593ace3399439621ec9d4195c188be12781cb165e559161381482bc513bb9a35d3746c30d88cc475642d63016c8a75347c1bb706bb3276218e6269e6e1a1c8912b077b5e82235ba837899c6c23bec2917457442f9f607d3d63b8c8ea5f065ca5e7a4ec037665a40003d9a0c95d97234ac9d50efa385cadfc79820a017abfd3f678ba5d7a8b890f5ca6c10ffbea407c6ac18638bde1806f739b7fd6ed49c665884f8cd4cdf77a9c02cad0ffa8971cdbe664ea7539521c87d9658808b112d1ea22b155208dfd693a48d5dd703279192ae5d9acab817c53193e17542db27d63764daccefb0be38b2bbdbd01345919e2f08811f99fb54a33033f6abc76c5ab4ebc9056f0e8d3005f80ffaf403ea744ff86a30fdc23f319972c4ccc81b3f6a23ad9fb00e480cd0b7d138e9db6e7cd25e424d29205782b06e4b0c5b932820ee2035fe657ec36489f60829d8c31dfe36accbc0fb78176508b6024b60909dba12747745d454c150f54945ef6a9a085e3a6b6539ace61f767e1e5fb1")
(hexString
"054355bf26622674d58d4b5037457138e50abfefe92b515c74c608fcd2a5af50eab0bc85d57ba3bda52fa00c9c15d44a24586b2cacc6be00209ae49202430645d4a5022703b1cace87829dc783d5092d")
(hexString
"97e5f003d16720844ba1bd157688a7697133f4bb4a33a7c91974937a1351d7af56d16d4a10bd196ddda700fcd8be517f8f9e39a17ba0eea235d98450a626be3a998ac31f35e8e082106a31fe94da11d02b73748db4aa519df6bbf25c1d62a2cf0b192c6a486bca2632fee9e4124ce2dba6f3366a14850f6a3b784d863119f52458ed774f8d63105b4f6a3d2e09cc74e3a02ec8386213087b4c849172ded6724a45c9c12744ec4a0f86a29b803b17187df5dd5f90e71d1f3f4578d4e1496e8892")
it "Sap output 1" $ do
case getSaplingFrontier tree of
Nothing -> assertFailure "failed to read comm tree"
Just tree' -> do
let pos =
sf_pos <$>
updateSaplingCommitmentTree
tree'
(fromText
"fa430c51bb108db782764cff55de9c6b11bbecd2493d2e0fa9f646428feef858")
case pos of
Nothing -> assertFailure "couldn't get note position"
Just p -> do
let dn =
decodeSaplingOutputEsk sk so1 TestNet External $
fromIntegral p
dn `shouldBe` Nothing
it "Sap output 2" $ do
case readZebraTransaction txHex2 of
Nothing -> assertFailure "Failed to read Tx"
Just tx -> do
let sBundle = fromRawSBundle $ zt_sBundle tx
case sBundle of
Nothing -> assertFailure "Failed to get sapling bundle"
Just sB -> do
let sOuts = sbOutputs sB
case getSaplingFrontier tree of
Nothing -> assertFailure "Failed to read tree"
Just tree' -> do
let pos =
getSaplingNotePosition <$>
(getSaplingWitness =<<
updateSaplingCommitmentTree
tree'
(fromText
"d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843"))
case pos of
Nothing -> assertFailure "couldn't get note position"
Just p -> do
let dn =
decodeSaplingOutputEsk
sk
(head . tail $ sOuts)
TestNet
External
p
dn `shouldBe` Nothing
it "Decode Sapling Output from Zingo" $ do
case readZebraTransaction txHex of
Nothing -> assertFailure "Failed to read Tx"
Just tx -> do
case sK of
Nothing -> assertFailure "Failed to generate spending key"
Just sK' -> do
let sBundle = fromRawSBundle $ zt_sBundle tx
case sBundle of
Nothing -> assertFailure "Failed to get sapling bundle"
Just sB -> do
let sOuts = sbOutputs sB
case getSaplingFrontier tree of
Nothing -> assertFailure "failed to read comm tree"
Just tree' -> do
let pos =
sf_pos <$>
updateSaplingCommitmentTree
tree'
(fromText
"d163c69029e8cb05d874b798c7973b3b1b1b0e04f984a252b73c848698320843")
case pos of
Nothing -> assertFailure "couldn't get note position"
Just p -> do
let dn =
decodeSaplingOutputEsk
sK'
(head . tail $ sOuts)
MainNet
External
(fromIntegral p)
dn `shouldNotBe` Nothing
describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do
let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e"
it "Try to generate valid ExchangeAddress from Transparent Address" $ do
case ta of
Nothing -> assertFailure "Failed to decode transparent address"
Just t -> do
case (tr_type (ta_receiver t)) of
P2SH ->
assertFailure
"P2SH not supported for ExchengeAddress generation"
P2PKH -> do
let exch =
encodeExchangeAddress (ta_network t) (ta_receiver t)
case exch of
Nothing -> assertFailure "Failed to encode Exchange address"
Just addr -> do
let eadr = decodeExchangeAddress (E.encodeUtf8 addr)
eadr `shouldNotBe` Nothing
describe "Tree updates" $ do
it "Orchard" $ do
let tree =
OrchardCommitmentTree $
hexString
"0136a7886d7d73bc1845223165fd9cb0cef02046c707e8f88a7f61564720bd0f3501dca1fbdd7b5ba92a0809af5e85874626ce0db14d0532a48e41dde6f0f81b46011f0001fb48c27bd07e68f27aba47cd6e93fa961e0ef8c63f993963a614e56855d2013c0001ea572db9c5c2d24c7ad9132ae32b27179466bf67a580d59901d13b281d3f530b01c160348f10b9ad893d9731317ebe36ac8665e01c52cbe15a56aa9b72e4e6c41e000001cd7695156de2debdc5b13ea84d32e4e3ac020fb0aa7cd372c57ce765103bd70401746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
let cmx1 =
hexString
"1712ead46028d4349e234abf59e94e0640fe7a0829e2e2e17e1a931631810400"
let cmx2 =
hexString
"39f5ad39817fb432fa07c5feb3a957189fbe7662a4b5555ca95093b6d853cf07"
let cmx3 =
hexString
"84f7fbc4b9f87215c653078d7fdd90756c3ba370c745065167da9eb73a65a83f"
let cmx4 =
hexString
"e55ad64e1ea2b261893fdea6ad0509b66e5f62d3142f351298c7135c4498d429"
let finalTree =
getOrchardFrontier $
OrchardCommitmentTree $
hexString
"0184f7fbc4b9f87215c653078d7fdd90756c3ba370c745065167da9eb73a65a83f01e55ad64e1ea2b261893fdea6ad0509b66e5f62d3142f351298c7135c4498d4291f0000014b1a76d3820087b26cd087ca84e17f3067a25ebed82ad23a93fa485affb5530b01ea572db9c5c2d24c7ad9132ae32b27179466bf67a580d59901d13b281d3f530b01c160348f10b9ad893d9731317ebe36ac8665e01c52cbe15a56aa9b72e4e6c41e000001cd7695156de2debdc5b13ea84d32e4e3ac020fb0aa7cd372c57ce765103bd70401746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
case getOrchardFrontier tree of
Nothing -> assertFailure "Failed to get frontier"
Just t1 ->
case updateOrchardCommitmentTree t1 cmx1 of
Nothing -> assertFailure "Failed to update frontier with cmx"
Just t2 -> do
case getOrchardWitness t2 of
Nothing -> assertFailure "Failed to get witness"
Just wit -> do
let uWit = updateOrchardWitness wit [cmx2, cmx3, cmx4]
Just (getOrchardWitnessAnchor uWit) `shouldBe`
getOrchardTreeAnchor <$>
finalTree
describe "Witness updates" $ do
it "Sapling" $ do
let wit =
SaplingWitness $
hexString
"01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e57ec972a9b9383dc9cb228980d2d7752bb2abebc4a604ca48c5457039d2e05b000301392bed8592185dde5ab7fc81aed75e98fcf041f1a3fda55ad0b0b139ba9380130001808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15"
let cmus =
[ hexString
"958ccdc752f2f593f6c1c8e2d7201348cd896e54c6d3c92200bdbe8b859eac44"
, hexString
"e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447"
, hexString
"0c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e18"
]
updateSaplingWitness wit cmus `shouldBe`
SaplingWitness
(hexString
"01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447010c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e1803000121c06ee1f1584f79d50785797a694c742be2ded600367ab7d54f3ed49e3adf7201808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15")
it "Orchard" $ do
let wit =
OrchardWitness $
hexString
"016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e000200019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301"
let cmxs =
[ hexString
"712ba86615ff4447e8d7c7b59f3873f03c03a173438b8e4c8d416756ed4fae10"
, hexString
"c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e"
, hexString
"ac20b8170b008888c19fc6e16f5e30a5ef1653e5219d0cd0c9353c3aa8f79823"
]
updateOrchardWitness wit cmxs `shouldBe`
OrchardWitness
(hexString
"016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e01ac20b8170b008888c19fc6e16f5e30a5ef1653e5219d0cd0c9353c3aa8f7982302010cfb50d8c877eb39e9c07082a032dd99d34be7c19fa7f30e9fecf5f14736240f019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301")
-- | Properties
prop_PhraseLength :: Property
prop_PhraseLength =
ioProperty $ do
p <- generateWalletSeedPhrase
return $ BS.length (getBytes p) >= 95
prop_SeedLength :: Property
prop_SeedLength =
ioProperty $ do
p <- generateWalletSeedPhrase
let s = getWalletSeed p
return $ maybe 0 (BS.length . getBytes) s === 64
prop_OrchardSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
prop_OrchardSpendingKey s c (NonNegative i) =
genOrchardSpendingKey s c i =/= Nothing
prop_OrchardReceiver ::
Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property
prop_OrchardReceiver s c (NonNegative i) (NonNegative j) scope =
genOrchardReceiver
j
scope
(fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) =/=
Nothing
prop_SaplingSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
prop_SaplingSpendingKey s c (NonNegative i) =
genSaplingSpendingKey s c i =/= Nothing
prop_SaplingReceiver ::
Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property
prop_SaplingReceiver s c (NonNegative i) (NonNegative j) =
genSaplingPaymentAddress
i
(fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c j) =/=
Nothing
prop_SaplingRecRepeated :: Seed -> CoinType -> NonNegative Int -> Property
prop_SaplingRecRepeated s c (NonNegative i) =
genSaplingPaymentAddress
i
(fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c 1) =/=
genSaplingPaymentAddress
(i + 1)
(fromMaybe (SaplingSpendingKey "") $ genSaplingSpendingKey s c 1)
prop_OrchardRecRepeated ::
Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Scope -> Property
prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) scope =
genOrchardReceiver
j
scope
(fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) =/=
genOrchardReceiver
(j + 1)
scope
(fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i)
prop_TransparentSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
prop_TransparentSpendingKey s coinType (NonNegative i) =
ioProperty $ do
k <- genTransparentPrvKey s coinType i
return $ xPrvChild k == fromIntegral i
prop_TransparentReceiver ::
Seed -> CoinType -> Scope -> NonNegative Int -> NonNegative Int -> Property
prop_TransparentReceiver s coinType scope (NonNegative i) (NonNegative j) =
ioProperty $ do
k <- genTransparentPrvKey s coinType i
r <- genTransparentReceiver j scope k
return $ tr_type r == P2PKH
-- | Generators
genOrcArgs :: Gen (CoinType, Int, Int)
genOrcArgs = do
i <- arbitrarySizedNatural
j <- arbitrarySizedNatural
c <- elements [MainNetCoin, TestNetCoin, RegTestNetCoin]
return (c, i, j)
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]
instance Arbitrary Scope where
arbitrary = elements [External, Internal]