From 213afdadd90043b53632ae53128744dc63324a5e Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 8 Oct 2024 10:01:55 -0500 Subject: [PATCH] feat(core): shielding and deshielding --- src/Zenith/Core.hs | 349 +++++++++++++++++++++++++++----------------- src/Zenith/GUI.hs | 13 +- src/Zenith/Types.hs | 6 + 3 files changed, 229 insertions(+), 139 deletions(-) diff --git a/src/Zenith/Core.hs b/src/Zenith/Core.hs index da1332b..783cf64 100644 --- a/src/Zenith/Core.hs +++ b/src/Zenith/Core.hs @@ -83,6 +83,7 @@ import Zenith.Types , RseedDB(..) , SaplingSpendingKeyDB(..) , ScopeDB(..) + , TransactionType(..) , TransparentSpendingKeyDB(..) , UnifiedAddressDB(..) , ValidAddressAPI(..) @@ -737,8 +738,9 @@ prepareTxV2 :: -> Int -> [ProposedNote] -> PrivacyPolicy + -> TransactionType -> NoLoggingT IO (Either TxError HexString) -prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do +prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy txType = do accRead <- liftIO $ getAccountById pool za let recipients = map extractReceiver pnotes logDebugN $ T.pack $ show recipients @@ -760,13 +762,19 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do --let fee = calculateTxFee firstPass $ fst recipient --logDebugN $ T.pack $ "calculated fee " ++ show fee notePlan <- - liftIO $ - selectUnspentNotesV2 - pool - za - (zats + 10000) - (map (\(x, _, _, _) -> x) recipients) - policy + case txType of + Normal -> + liftIO $ + selectUnspentNotesV2 + pool + za + (zats + 10000) + (map (\(x, _, _, _) -> x) recipients) + policy + Shielding -> + liftIO $ selectUnspentNotesV2 pool za (zats + 10000) [3] Medium + Deshielding -> + liftIO $ selectUnspentNotesV2 pool za (zats + 10000) [1] None case notePlan of Right (tList, sList, oList) -> do logDebugN "selected notes" @@ -795,7 +803,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do --print oSpends dummy' <- liftIO $ - makeOutgoing acc recipients (noteTotal - 5000 - zats) policy + makeOutgoing acc recipients (noteTotal - 5000 - zats) policy txType case dummy' of Left e -> return $ Left e Right dummy -> do @@ -818,13 +826,21 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do fromIntegral (runGet getInt64le $ LBS.fromStrict $ toBytes fee) finalNotePlan <- - liftIO $ - selectUnspentNotesV2 - pool - za - (zats + feeAmt) - (map (\(x, _, _, _) -> x) recipients) - policy + case txType of + Normal -> + liftIO $ + selectUnspentNotesV2 + pool + za + (zats + feeAmt) + (map (\(x, _, _, _) -> x) recipients) + policy + Shielding -> + liftIO $ + selectUnspentNotesV2 pool za (zats + feeAmt) [3] Medium + Deshielding -> + liftIO $ + selectUnspentNotesV2 pool za (zats + feeAmt) [1] None case finalNotePlan of Right (tList1, sList1, oList1) -> do logDebugN $ @@ -855,6 +871,7 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do recipients (noteTotal1 - feeAmt - zats) policy + txType logDebugN $ T.pack $ show outgoing' case outgoing' of Left e -> return $ Left e @@ -931,131 +948,189 @@ prepareTxV2 pool zebraHost zebraPort zn za bh pnotes policy = do -> [(Int, BS.ByteString, Int, T.Text)] -> Integer -> PrivacyPolicy + -> TransactionType -> IO (Either TxError [OutgoingNote]) - makeOutgoing acc recvs chg pol = do + makeOutgoing acc recvs chg pol tt = do let k = map (\(x, _, _, _) -> x) recvs + let j = map (\(_, _, x, _) -> x) recvs chgAddr <- runNoLoggingT $ getInternalAddresses pool $ entityKey acc let internalUA = getUA $ walletAddressUAddress $ entityVal $ head chgAddr - case pol of - Full -> - if elem 1 k || elem 2 k || elem 5 k || elem 6 k - then return $ - Left $ - PrivacyPolicyError - "Receiver not compatible with privacy policy" - else if elem 3 k && elem 4 k - then return $ - Left $ - PrivacyPolicyError - "Multiple shielded pulls not allowed for Full privacy" - else if 3 `elem` k - then do - let chgRcvr = - fromJust $ - s_rec =<< - isValidUnifiedAddress - (E.encodeUtf8 internalUA) - let cnote = - OutgoingNote - 3 - (getBytes $ - getSapSK $ - zcashAccountSapSpendKey $ entityVal acc) - (getBytes chgRcvr) - (fromIntegral chg) - "" - True - let onotes = - map - (prepareOutgoingNote (entityVal acc)) - recvs - return $ Right $ cnote : onotes - else if 4 `elem` k - then do - let chgRcvr = - fromJust $ - o_rec =<< - isValidUnifiedAddress - (E.encodeUtf8 internalUA) - let cnote = - OutgoingNote - 4 - (getBytes $ - getOrchSK $ - zcashAccountOrchSpendKey $ - entityVal acc) - (getBytes chgRcvr) - (fromIntegral chg) - "" - True - let onotes = - map - (prepareOutgoingNote (entityVal acc)) - recvs - return $ Right $ cnote : onotes - else return $ Left ZHError - Medium -> - if elem 1 k || elem 2 k || elem 5 k || elem 6 k - then return $ - Left $ - PrivacyPolicyError - "Receiver not compatible with privacy policy" - else do - let chgRcvr = - fromJust $ - o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) - let cnote = - OutgoingNote - 4 - (getBytes $ - getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc) - (getBytes chgRcvr) - (fromIntegral chg) - "" - True - let onotes = map (prepareOutgoingNote (entityVal acc)) recvs - return $ Right $ cnote : onotes - Low -> - if elem 5 k || elem 6 k - then return $ - Left $ - PrivacyPolicyError - "Receiver not compatible with privacy policy" - else do - let chgRcvr = - fromJust $ - o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) - let cnote = - OutgoingNote - 4 - (getBytes $ - getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc) - (getBytes chgRcvr) - (fromIntegral chg) - "" - True - let onotes = map (prepareOutgoingNote (entityVal acc)) recvs - return $ Right $ cnote : onotes - None -> - if elem 3 k || elem 4 k - then return $ - Left $ - PrivacyPolicyError - "Receiver not compatible with privacy policy" - else do - let chgRcvr = - fromJust $ - t_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) - let cnote = - OutgoingNote - 1 - BS.empty - (toBytes $ tr_bytes chgRcvr) - (fromIntegral chg) - "" - True - let onotes = map (prepareOutgoingNote (entityVal acc)) recvs - return $ Right $ cnote : onotes + case tt of + Deshielding -> do + let chgRcvr = + fromJust $ + o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) + let trRcvr = + fromJust $ + t_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 4 + (getBytes $ + getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc) + (getBytes chgRcvr) + (fromIntegral chg) + "" + True + let tnote = + OutgoingNote + 1 + BS.empty + (toBytes $ tr_bytes trRcvr) + (fromIntegral $ head j) + "" + True + return $ Right [cnote, tnote] + Shielding -> do + let chgRcvr = + fromJust $ + t_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) + let oRcvr = + fromJust $ + o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 1 + BS.empty + (toBytes $ tr_bytes chgRcvr) + (fromIntegral chg) + "" + True + let snote = + OutgoingNote + 4 + (getBytes $ + getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc) + (getBytes oRcvr) + (fromIntegral $ head j) + "" + True + return $ Right [cnote, snote] + Normal -> + case pol of + Full -> + if elem 1 k || elem 2 k || elem 5 k || elem 6 k + then return $ + Left $ + PrivacyPolicyError + "Receiver not compatible with privacy policy" + else if elem 3 k && elem 4 k + then return $ + Left $ + PrivacyPolicyError + "Multiple shielded pulls not allowed for Full privacy" + else if 3 `elem` k + then do + let chgRcvr = + fromJust $ + s_rec =<< + isValidUnifiedAddress + (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 3 + (getBytes $ + getSapSK $ + zcashAccountSapSpendKey $ entityVal acc) + (getBytes chgRcvr) + (fromIntegral chg) + "" + True + let onotes = + map + (prepareOutgoingNote (entityVal acc)) + recvs + return $ Right $ cnote : onotes + else if 4 `elem` k + then do + let chgRcvr = + fromJust $ + o_rec =<< + isValidUnifiedAddress + (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 4 + (getBytes $ + getOrchSK $ + zcashAccountOrchSpendKey $ + entityVal acc) + (getBytes chgRcvr) + (fromIntegral chg) + "" + True + let onotes = + map + (prepareOutgoingNote + (entityVal acc)) + recvs + return $ Right $ cnote : onotes + else return $ Left ZHError + Medium -> + if elem 1 k || elem 2 k || elem 5 k || elem 6 k + then return $ + Left $ + PrivacyPolicyError + "Receiver not compatible with privacy policy" + else do + let chgRcvr = + fromJust $ + o_rec =<< + isValidUnifiedAddress (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 4 + (getBytes $ + getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc) + (getBytes chgRcvr) + (fromIntegral chg) + "" + True + let onotes = map (prepareOutgoingNote (entityVal acc)) recvs + return $ Right $ cnote : onotes + Low -> + if elem 5 k || elem 6 k + then return $ + Left $ + PrivacyPolicyError + "Receiver not compatible with privacy policy" + else do + let chgRcvr = + fromJust $ + o_rec =<< + isValidUnifiedAddress (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 4 + (getBytes $ + getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc) + (getBytes chgRcvr) + (fromIntegral chg) + "" + True + let onotes = map (prepareOutgoingNote (entityVal acc)) recvs + return $ Right $ cnote : onotes + None -> + if elem 3 k || elem 4 k + then return $ + Left $ + PrivacyPolicyError + "Receiver not compatible with privacy policy" + else do + let chgRcvr = + fromJust $ + t_rec =<< + isValidUnifiedAddress (E.encodeUtf8 internalUA) + let cnote = + OutgoingNote + 1 + BS.empty + (toBytes $ tr_bytes chgRcvr) + (fromIntegral chg) + "" + True + let onotes = map (prepareOutgoingNote (entityVal acc)) recvs + return $ Right $ cnote : onotes getTotalAmount :: ( [Entity WalletTrNote] , [Entity WalletSapNote] diff --git a/src/Zenith/GUI.hs b/src/Zenith/GUI.hs index 1b7a108..b671914 100644 --- a/src/Zenith/GUI.hs +++ b/src/Zenith/GUI.hs @@ -1106,6 +1106,7 @@ handleEvent wenv node model evt = (model ^. sendRecipient) (model ^. sendMemo) (model ^. privacyChoice) + Normal , Event CancelSend ] CancelSend -> @@ -1559,9 +1560,10 @@ sendTransaction :: -> T.Text -> T.Text -> PrivacyPolicy + -> TransactionType -> (AppEvent -> IO ()) -> IO () -sendTransaction config znet accId bl amt ua memo policy sendMsg = do +sendTransaction config znet accId bl amt ua memo policy txType sendMsg = do sendMsg $ ShowModal "Preparing transaction..." case parseAddress (E.encodeUtf8 ua) of Nothing -> sendMsg $ ShowError "Incorrect address" @@ -1579,8 +1581,15 @@ sendTransaction config znet accId bl amt ua memo policy sendMsg = do znet accId bl - [ProposedNote (ValidAddressAPI addr) amt (Just memo)] + [ ProposedNote + (ValidAddressAPI addr) + amt + (if memo == "" + then Nothing + else Just memo) + ] policy + txType case res of Left e -> sendMsg $ ShowError $ T.pack $ show e Right rawTx -> do diff --git a/src/Zenith/Types.hs b/src/Zenith/Types.hs index 18c193d..48d19b8 100644 --- a/src/Zenith/Types.hs +++ b/src/Zenith/Types.hs @@ -262,6 +262,12 @@ instance ToJSON ProposedNote where toJSON (ProposedNote a n m) = object ["address" .= a, "amount" .= n, "memo" .= m] +data TransactionType + = Normal + | Shielding + | Deshielding + deriving (Eq, Prelude.Show) + -- ** `zebrad` -- | Type for modeling the tree state response data ZebraTreeInfo = ZebraTreeInfo