2024-03-04 17:59:07 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
2024-01-18 18:55:23 +00:00
|
|
|
-- Copyright 2022-2024 Vergara Technologies LLC
|
|
|
|
--
|
|
|
|
-- This file is part of Zcash-Haskell.
|
|
|
|
--
|
2023-08-17 15:02:32 +00:00
|
|
|
-- |
|
|
|
|
-- Module : ZcashHaskell.Orchard
|
2023-12-20 20:03:42 +00:00
|
|
|
-- Copyright : 2022-2024 Vergara Technologies
|
2024-01-18 18:55:23 +00:00
|
|
|
-- License : MIT
|
2023-08-17 15:02:32 +00:00
|
|
|
--
|
|
|
|
-- Maintainer : rene@vergara.network
|
|
|
|
-- Stability : experimental
|
|
|
|
-- Portability : unknown
|
|
|
|
--
|
|
|
|
-- Functions to interact with the Orchard shielded pool of the Zcash blockchain.
|
|
|
|
--
|
2023-06-14 15:53:29 +00:00
|
|
|
module ZcashHaskell.Orchard where
|
2023-05-04 20:26:49 +00:00
|
|
|
|
|
|
|
import C.Zcash
|
2024-03-07 22:06:33 +00:00
|
|
|
( rustWrapperGenOrchardReceiver
|
|
|
|
, rustWrapperGenOrchardSpendKey
|
2024-03-05 20:44:00 +00:00
|
|
|
, rustWrapperOrchardCheck
|
2023-05-04 20:26:49 +00:00
|
|
|
, rustWrapperOrchardNoteDecode
|
2024-01-12 15:46:26 +00:00
|
|
|
, rustWrapperUADecode
|
2023-05-04 20:26:49 +00:00
|
|
|
, rustWrapperUfvkDecode
|
|
|
|
)
|
|
|
|
import qualified Data.ByteString as BS
|
2024-03-04 17:59:07 +00:00
|
|
|
import qualified Data.ByteString.Char8 as C
|
2024-03-12 21:03:35 +00:00
|
|
|
import Data.HexString (fromRawBytes, toBytes)
|
2024-03-04 17:59:07 +00:00
|
|
|
import qualified Data.Text as T
|
|
|
|
import qualified Data.Text.Encoding as E
|
|
|
|
import Data.Word
|
2023-05-04 20:26:49 +00:00
|
|
|
import Foreign.Rust.Marshall.Variable
|
2023-06-14 15:53:29 +00:00
|
|
|
import ZcashHaskell.Types
|
2024-03-04 17:59:07 +00:00
|
|
|
import ZcashHaskell.Utils (encodeBech32m, f4Jumble)
|
2023-05-04 20:26:49 +00:00
|
|
|
|
2024-03-05 20:44:00 +00:00
|
|
|
-- | Derives an Orchard spending key for the given seed and account ID
|
2024-03-07 22:06:33 +00:00
|
|
|
genOrchardSpendingKey ::
|
|
|
|
Seed -> CoinType -> AccountId -> Maybe OrchardSpendingKey
|
2024-03-05 20:44:00 +00:00
|
|
|
genOrchardSpendingKey s coinType accountId =
|
2024-03-05 21:09:35 +00:00
|
|
|
if BS.length k /= 32
|
|
|
|
then Nothing
|
|
|
|
else Just k
|
|
|
|
where
|
|
|
|
k =
|
|
|
|
withPureBorshVarBuffer $
|
|
|
|
rustWrapperGenOrchardSpendKey
|
|
|
|
s
|
|
|
|
(getValue coinType)
|
|
|
|
(fromIntegral accountId)
|
2024-03-05 20:44:00 +00:00
|
|
|
|
2024-03-07 22:06:33 +00:00
|
|
|
-- | Derives an Orchard receiver for the given spending key and index
|
|
|
|
genOrchardReceiver :: Int -> OrchardSpendingKey -> Maybe OrchardReceiver
|
|
|
|
genOrchardReceiver i osk =
|
|
|
|
if BS.length k /= 43
|
|
|
|
then Nothing
|
|
|
|
else Just k
|
|
|
|
where
|
|
|
|
k =
|
|
|
|
withPureBorshVarBuffer $
|
|
|
|
rustWrapperGenOrchardReceiver osk (fromIntegral i)
|
|
|
|
|
2023-08-17 15:02:32 +00:00
|
|
|
-- | Checks if given bytestring is a valid encoded unified address
|
2024-01-12 15:46:26 +00:00
|
|
|
isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress
|
|
|
|
isValidUnifiedAddress str =
|
|
|
|
case raw_net decodedAddress of
|
|
|
|
0 -> Nothing
|
|
|
|
_ -> Just $ makeUA decodedAddress
|
|
|
|
where
|
|
|
|
decodedAddress = (withPureBorshVarBuffer . rustWrapperUADecode) str
|
|
|
|
whichNet =
|
|
|
|
case raw_net decodedAddress of
|
|
|
|
1 -> MainNet
|
|
|
|
2 -> TestNet
|
|
|
|
3 -> RegTestNet
|
|
|
|
makeUA x =
|
|
|
|
UnifiedAddress
|
|
|
|
whichNet
|
2024-03-08 19:35:37 +00:00
|
|
|
(if BS.length (raw_o x) == 43
|
|
|
|
then Just (raw_o x)
|
|
|
|
else Nothing)
|
|
|
|
(if BS.length (raw_s x) == 43
|
|
|
|
then Just (raw_s x)
|
|
|
|
else Nothing)
|
2024-01-12 15:46:26 +00:00
|
|
|
(if not (BS.null (raw_t x))
|
2024-03-12 21:03:35 +00:00
|
|
|
then Just $ TransparentAddress P2PKH (fromRawBytes $ raw_t x)
|
2024-01-12 15:46:26 +00:00
|
|
|
else if not (BS.null (raw_to x))
|
2024-03-12 21:03:35 +00:00
|
|
|
then Just $ TransparentAddress P2SH (fromRawBytes $ raw_to x)
|
2024-01-12 15:46:26 +00:00
|
|
|
else Nothing)
|
2023-05-04 20:26:49 +00:00
|
|
|
|
2024-03-04 17:59:07 +00:00
|
|
|
-- | Encode a 'UnifiedAddress' per [ZIP-316](https://zips.z.cash/zip-0316)
|
|
|
|
encodeUnifiedAddress :: UnifiedAddress -> T.Text
|
|
|
|
encodeUnifiedAddress ua = encodeBech32m (E.encodeUtf8 hr) b
|
|
|
|
where
|
|
|
|
hr =
|
|
|
|
case ua_net ua of
|
2024-03-06 21:10:26 +00:00
|
|
|
MainNet -> uniPaymentAddressHrp
|
|
|
|
TestNet -> uniTestPaymentAddressHrp
|
2024-03-04 17:59:07 +00:00
|
|
|
b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding
|
|
|
|
tReceiver =
|
|
|
|
case t_rec ua of
|
|
|
|
Nothing -> BS.empty
|
|
|
|
Just t ->
|
|
|
|
case ta_type t of
|
2024-03-12 21:03:35 +00:00
|
|
|
P2SH -> packReceiver 0x01 $ Just $ toBytes $ ta_bytes t
|
|
|
|
P2PKH -> packReceiver 0x00 $ Just $ toBytes $ ta_bytes t
|
2024-03-04 17:59:07 +00:00
|
|
|
sReceiver = packReceiver 0x02 $ s_rec ua
|
|
|
|
oReceiver = packReceiver 0x03 $ o_rec ua
|
|
|
|
padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr
|
2024-03-08 19:35:37 +00:00
|
|
|
packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString
|
|
|
|
packReceiver typeCode receiver' =
|
|
|
|
case receiver' of
|
|
|
|
Just receiver ->
|
|
|
|
if BS.length receiver > 1
|
|
|
|
then BS.singleton typeCode `BS.append`
|
|
|
|
(BS.singleton . toEnum . BS.length) receiver `BS.append`
|
|
|
|
receiver
|
|
|
|
else BS.empty
|
|
|
|
Nothing -> BS.empty
|
2024-03-04 17:59:07 +00:00
|
|
|
|
2023-08-17 15:02:32 +00:00
|
|
|
-- | Attempts to decode the given bytestring into a Unified Full Viewing Key
|
2023-05-04 20:26:49 +00:00
|
|
|
decodeUfvk :: BS.ByteString -> Maybe UnifiedFullViewingKey
|
|
|
|
decodeUfvk str =
|
|
|
|
case net decodedKey of
|
|
|
|
0 -> Nothing
|
|
|
|
_ -> Just decodedKey
|
|
|
|
where
|
|
|
|
decodedKey = (withPureBorshVarBuffer . rustWrapperUfvkDecode) str
|
|
|
|
|
2023-10-04 16:12:30 +00:00
|
|
|
-- | Check if the given UVK matches the UA given
|
|
|
|
matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool
|
|
|
|
matchOrchardAddress = rustWrapperOrchardCheck
|
|
|
|
|
2023-08-17 15:02:32 +00:00
|
|
|
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
|
2023-05-04 20:26:49 +00:00
|
|
|
decryptOrchardAction ::
|
2023-09-26 20:24:18 +00:00
|
|
|
UnifiedFullViewingKey -> OrchardAction -> Maybe DecodedNote
|
|
|
|
decryptOrchardAction key encAction =
|
2023-05-04 20:26:49 +00:00
|
|
|
case a_value decodedAction of
|
|
|
|
0 -> Nothing
|
|
|
|
_ -> Just decodedAction
|
|
|
|
where
|
|
|
|
decodedAction =
|
|
|
|
withPureBorshVarBuffer $
|
|
|
|
rustWrapperOrchardNoteDecode (o_key key) encAction
|