RPC: Shield and de-shield funds #110
7 changed files with 164 additions and 78 deletions
|
@ -1285,28 +1285,9 @@ appEvent (BT.VtyEvent e) = do
|
||||||
Just (_j, w1) -> return w1
|
Just (_j, w1) -> return w1
|
||||||
Just (_k, w) -> return w
|
Just (_k, w) -> return w
|
||||||
fs1 <- BT.zoom deshieldForm $ BT.gets formState
|
fs1 <- BT.zoom deshieldForm $ BT.gets formState
|
||||||
let tAddrMaybe =
|
|
||||||
Transparent <$>
|
|
||||||
((decodeTransparentAddress .
|
|
||||||
E.encodeUtf8 .
|
|
||||||
encodeTransparentReceiver (s ^. network)) =<<
|
|
||||||
(t_rec =<<
|
|
||||||
(isValidUnifiedAddress .
|
|
||||||
E.encodeUtf8 .
|
|
||||||
getUA . walletAddressUAddress)
|
|
||||||
(entityVal selAddr)))
|
|
||||||
bl <-
|
bl <-
|
||||||
liftIO $
|
liftIO $
|
||||||
getChainTip (s ^. zebraHost) (s ^. zebraPort)
|
getChainTip (s ^. zebraHost) (s ^. zebraPort)
|
||||||
case tAddrMaybe of
|
|
||||||
Nothing -> do
|
|
||||||
BT.modify $
|
|
||||||
set
|
|
||||||
msg
|
|
||||||
"Failed to obtain transparent address"
|
|
||||||
BT.modify $ set displayBox MsgDisplay
|
|
||||||
BT.modify $ set dialogBox Blank
|
|
||||||
Just tAddr -> do
|
|
||||||
_ <-
|
_ <-
|
||||||
liftIO $
|
liftIO $
|
||||||
forkIO $
|
forkIO $
|
||||||
|
@ -1318,10 +1299,7 @@ appEvent (BT.VtyEvent e) = do
|
||||||
(s ^. network)
|
(s ^. network)
|
||||||
(entityKey selAcc)
|
(entityKey selAcc)
|
||||||
bl
|
bl
|
||||||
(ProposedNote
|
|
||||||
(ValidAddressAPI tAddr)
|
|
||||||
(fs1 ^. shAmt)
|
(fs1 ^. shAmt)
|
||||||
Nothing)
|
|
||||||
BT.modify $ set displayBox SendDisplay
|
BT.modify $ set displayBox SendDisplay
|
||||||
BT.modify $ set dialogBox Blank
|
BT.modify $ set dialogBox Blank
|
||||||
else do
|
else do
|
||||||
|
@ -2075,7 +2053,7 @@ deshieldTransaction ::
|
||||||
-> ZcashNet
|
-> ZcashNet
|
||||||
-> ZcashAccountId
|
-> ZcashAccountId
|
||||||
-> Int
|
-> Int
|
||||||
-> ProposedNote
|
-> Scientific
|
||||||
-> IO ()
|
-> IO ()
|
||||||
deshieldTransaction pool chan zHost zPort znet accId bl pnote = do
|
deshieldTransaction pool chan zHost zPort znet accId bl pnote = do
|
||||||
BC.writeBChan chan $ TickMsg "Deshielding funds..."
|
BC.writeBChan chan $ TickMsg "Deshielding funds..."
|
||||||
|
|
|
@ -44,6 +44,7 @@ import ZcashHaskell.Orchard
|
||||||
, getOrchardTreeParts
|
, getOrchardTreeParts
|
||||||
, getOrchardWitness
|
, getOrchardWitness
|
||||||
, isValidUnifiedAddress
|
, isValidUnifiedAddress
|
||||||
|
, parseAddress
|
||||||
, updateOrchardCommitmentTree
|
, updateOrchardCommitmentTree
|
||||||
, updateOrchardWitness
|
, updateOrchardWitness
|
||||||
)
|
)
|
||||||
|
@ -86,6 +87,7 @@ import Zenith.Types
|
||||||
, ZenithStatus(..)
|
, ZenithStatus(..)
|
||||||
, ZenithUuid(..)
|
, ZenithUuid(..)
|
||||||
)
|
)
|
||||||
|
import Zenith.Utils (getTransparentFromUA)
|
||||||
|
|
||||||
-- * Zebra Node interaction
|
-- * Zebra Node interaction
|
||||||
-- | Checks the status of the `zebrad` node
|
-- | Checks the status of the `zebrad` node
|
||||||
|
@ -751,14 +753,37 @@ deshieldNotes ::
|
||||||
-> ZcashNet
|
-> ZcashNet
|
||||||
-> ZcashAccountId
|
-> ZcashAccountId
|
||||||
-> Int
|
-> Int
|
||||||
-> ProposedNote
|
-> Scientific
|
||||||
-> NoLoggingT IO (Either TxError HexString)
|
-> NoLoggingT IO (Either TxError HexString)
|
||||||
deshieldNotes pool zebraHost zebraPort znet za bh pnote = do
|
deshieldNotes pool zebraHost zebraPort znet za bh pnote = do
|
||||||
bal <- liftIO $ getShieldedBalance pool za
|
bal <- liftIO $ getShieldedBalance pool za
|
||||||
let zats = pn_amt pnote * scientific 1 8
|
addrs <- getAddresses pool za
|
||||||
|
let defAddr =
|
||||||
|
parseAddress $
|
||||||
|
E.encodeUtf8 $ getUA $ walletAddressUAddress $ entityVal $ head addrs
|
||||||
|
case defAddr of
|
||||||
|
Nothing -> return $ Left ZHError
|
||||||
|
Just (Unified x) -> do
|
||||||
|
case getTransparentFromUA x of
|
||||||
|
Nothing -> return $ Left ZHError
|
||||||
|
Just ta -> do
|
||||||
|
let zats = pnote * scientific 1 8
|
||||||
if fromInteger bal > (scientific 2 4 + zats)
|
if fromInteger bal > (scientific 2 4 + zats)
|
||||||
then prepareTxV2 pool zebraHost zebraPort znet za bh [pnote] Low
|
then prepareTxV2
|
||||||
|
pool
|
||||||
|
zebraHost
|
||||||
|
zebraPort
|
||||||
|
znet
|
||||||
|
za
|
||||||
|
bh
|
||||||
|
[ ProposedNote
|
||||||
|
(ValidAddressAPI $ Transparent ta)
|
||||||
|
pnote
|
||||||
|
Nothing
|
||||||
|
]
|
||||||
|
Low
|
||||||
else return $ Left InsufficientFunds
|
else return $ Left InsufficientFunds
|
||||||
|
_anyOther -> return $ Left ZHError
|
||||||
|
|
||||||
shieldTransparentNotes ::
|
shieldTransparentNotes ::
|
||||||
ConnectionPool
|
ConnectionPool
|
||||||
|
|
|
@ -630,6 +630,7 @@ getAddresses pool a =
|
||||||
addrs <- from $ table @WalletAddress
|
addrs <- from $ table @WalletAddress
|
||||||
where_ (addrs ^. WalletAddressAccId ==. val a)
|
where_ (addrs ^. WalletAddressAccId ==. val a)
|
||||||
where_ (addrs ^. WalletAddressScope ==. val (ScopeDB External))
|
where_ (addrs ^. WalletAddressScope ==. val (ScopeDB External))
|
||||||
|
orderBy [asc $ addrs ^. WalletAddressId]
|
||||||
pure addrs
|
pure addrs
|
||||||
|
|
||||||
getAddressById ::
|
getAddressById ::
|
||||||
|
|
|
@ -1784,27 +1784,7 @@ deshieldTransaction config znet accId addR pnote sendMsg = do
|
||||||
let zPort = c_zebraPort config
|
let zPort = c_zebraPort config
|
||||||
pool <- runNoLoggingT $ initPool dbPath
|
pool <- runNoLoggingT $ initPool dbPath
|
||||||
bl <- getChainTip zHost zPort
|
bl <- getChainTip zHost zPort
|
||||||
let tAddrMaybe =
|
res <- runNoLoggingT $ deshieldNotes pool zHost zPort znet accId bl pnote
|
||||||
Transparent <$>
|
|
||||||
((decodeTransparentAddress .
|
|
||||||
E.encodeUtf8 . encodeTransparentReceiver znet) =<<
|
|
||||||
(t_rec =<<
|
|
||||||
(isValidUnifiedAddress .
|
|
||||||
E.encodeUtf8 . getUA . walletAddressUAddress)
|
|
||||||
(entityVal addr)))
|
|
||||||
case tAddrMaybe of
|
|
||||||
Nothing -> sendMsg $ ShowError "No transparent address available"
|
|
||||||
Just tAddr -> do
|
|
||||||
res <-
|
|
||||||
runNoLoggingT $
|
|
||||||
deshieldNotes
|
|
||||||
pool
|
|
||||||
zHost
|
|
||||||
zPort
|
|
||||||
znet
|
|
||||||
accId
|
|
||||||
bl
|
|
||||||
(ProposedNote (ValidAddressAPI tAddr) pnote Nothing)
|
|
||||||
case res of
|
case res of
|
||||||
Left e -> sendMsg $ ShowError $ T.pack (show e)
|
Left e -> sendMsg $ ShowError $ T.pack (show e)
|
||||||
Right rawTx -> do
|
Right rawTx -> do
|
||||||
|
|
|
@ -20,7 +20,7 @@ import Control.Monad.Logger (runFileLoggingT, runNoLoggingT, runStderrLoggingT)
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import qualified Data.HexString as H
|
import qualified Data.HexString as H
|
||||||
import Data.Int
|
import Data.Int
|
||||||
import Data.Scientific (floatingOrInteger)
|
import Data.Scientific (Scientific(..), floatingOrInteger)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import Data.Time.Clock (getCurrentTime)
|
import Data.Time.Clock (getCurrentTime)
|
||||||
|
@ -50,6 +50,7 @@ import Zenith.Core
|
||||||
( checkBlockChain
|
( checkBlockChain
|
||||||
, createCustomWalletAddress
|
, createCustomWalletAddress
|
||||||
, createZcashAccount
|
, createZcashAccount
|
||||||
|
, deshieldNotes
|
||||||
, prepareTxV2
|
, prepareTxV2
|
||||||
, shieldTransparentNotes
|
, shieldTransparentNotes
|
||||||
, syncWallet
|
, syncWallet
|
||||||
|
@ -123,6 +124,7 @@ data ZenithMethod
|
||||||
| GetOperationStatus
|
| GetOperationStatus
|
||||||
| SendMany
|
| SendMany
|
||||||
| ShieldNotes
|
| ShieldNotes
|
||||||
|
| DeshieldFunds
|
||||||
| UnknownMethod
|
| UnknownMethod
|
||||||
deriving (Eq, Prelude.Show)
|
deriving (Eq, Prelude.Show)
|
||||||
|
|
||||||
|
@ -139,6 +141,7 @@ instance ToJSON ZenithMethod where
|
||||||
toJSON GetOperationStatus = Data.Aeson.String "getoperationstatus"
|
toJSON GetOperationStatus = Data.Aeson.String "getoperationstatus"
|
||||||
toJSON SendMany = Data.Aeson.String "sendmany"
|
toJSON SendMany = Data.Aeson.String "sendmany"
|
||||||
toJSON ShieldNotes = Data.Aeson.String "shieldnotes"
|
toJSON ShieldNotes = Data.Aeson.String "shieldnotes"
|
||||||
|
toJSON DeshieldFunds = Data.Aeson.String "deshieldfunds"
|
||||||
toJSON UnknownMethod = Data.Aeson.Null
|
toJSON UnknownMethod = Data.Aeson.Null
|
||||||
|
|
||||||
instance FromJSON ZenithMethod where
|
instance FromJSON ZenithMethod where
|
||||||
|
@ -156,6 +159,7 @@ instance FromJSON ZenithMethod where
|
||||||
"getoperationstatus" -> pure GetOperationStatus
|
"getoperationstatus" -> pure GetOperationStatus
|
||||||
"sendmany" -> pure SendMany
|
"sendmany" -> pure SendMany
|
||||||
"shieldnotes" -> pure ShieldNotes
|
"shieldnotes" -> pure ShieldNotes
|
||||||
|
"deshieldfunds" -> pure DeshieldFunds
|
||||||
_ -> pure UnknownMethod
|
_ -> pure UnknownMethod
|
||||||
|
|
||||||
data ZenithParams
|
data ZenithParams
|
||||||
|
@ -172,6 +176,7 @@ data ZenithParams
|
||||||
| SendParams !Int ![ProposedNote] !PrivacyPolicy
|
| SendParams !Int ![ProposedNote] !PrivacyPolicy
|
||||||
| TestParams !T.Text
|
| TestParams !T.Text
|
||||||
| ShieldNotesParams !Int
|
| ShieldNotesParams !Int
|
||||||
|
| DeshieldParams !Int !Scientific
|
||||||
deriving (Eq, Prelude.Show)
|
deriving (Eq, Prelude.Show)
|
||||||
|
|
||||||
instance ToJSON ZenithParams where
|
instance ToJSON ZenithParams where
|
||||||
|
@ -197,6 +202,8 @@ instance ToJSON ZenithParams where
|
||||||
toJSON (SendParams i ns p) =
|
toJSON (SendParams i ns p) =
|
||||||
Data.Aeson.Array $ V.fromList [jsonNumber i, toJSON ns, toJSON p]
|
Data.Aeson.Array $ V.fromList [jsonNumber i, toJSON ns, toJSON p]
|
||||||
toJSON (ShieldNotesParams i) = Data.Aeson.Array $ V.fromList [jsonNumber i]
|
toJSON (ShieldNotesParams i) = Data.Aeson.Array $ V.fromList [jsonNumber i]
|
||||||
|
toJSON (DeshieldParams i s) =
|
||||||
|
Data.Aeson.Array $ V.fromList [jsonNumber i, Data.Aeson.Number s]
|
||||||
|
|
||||||
data ZenithResponse
|
data ZenithResponse
|
||||||
= InfoResponse !T.Text !ZenithInfo
|
= InfoResponse !T.Text !ZenithInfo
|
||||||
|
@ -510,6 +517,17 @@ instance FromJSON RpcCall where
|
||||||
pure $ RpcCall v i ShieldNotes (ShieldNotesParams x)
|
pure $ RpcCall v i ShieldNotes (ShieldNotesParams x)
|
||||||
else pure $ RpcCall v i ShieldNotes BadParams
|
else pure $ RpcCall v i ShieldNotes BadParams
|
||||||
_anyOther -> pure $ RpcCall v i ShieldNotes BadParams
|
_anyOther -> pure $ RpcCall v i ShieldNotes BadParams
|
||||||
|
DeshieldFunds -> do
|
||||||
|
p <- obj .: "params"
|
||||||
|
case p of
|
||||||
|
Array a ->
|
||||||
|
if V.length a == 2
|
||||||
|
then do
|
||||||
|
x <- parseJSON $ a V.! 0
|
||||||
|
y <- parseJSON $ a V.! 1
|
||||||
|
pure $ RpcCall v i DeshieldFunds (DeshieldParams x y)
|
||||||
|
else pure $ RpcCall v i DeshieldFunds BadParams
|
||||||
|
_anyOther -> pure $ RpcCall v i DeshieldFunds BadParams
|
||||||
|
|
||||||
type ZenithRPC
|
type ZenithRPC
|
||||||
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
|
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
|
||||||
|
@ -942,6 +960,87 @@ zenithServer state = getinfo :<|> handleRPC
|
||||||
"Account does not exist."
|
"Account does not exist."
|
||||||
_anyOtherParams ->
|
_anyOtherParams ->
|
||||||
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
||||||
|
DeshieldFunds -> do
|
||||||
|
case parameters req of
|
||||||
|
DeshieldParams i k -> do
|
||||||
|
let dbPath = w_dbPath state
|
||||||
|
let net = w_network state
|
||||||
|
let zHost = w_host state
|
||||||
|
let zPort = w_port state
|
||||||
|
pool <- liftIO $ runNoLoggingT $ initPool dbPath
|
||||||
|
syncChk <- liftIO $ isSyncing pool
|
||||||
|
if syncChk
|
||||||
|
then return $
|
||||||
|
ErrorResponse
|
||||||
|
(callId req)
|
||||||
|
(-32012)
|
||||||
|
"The Zenith server is syncing, please try again later."
|
||||||
|
else do
|
||||||
|
opid <- liftIO nextRandom
|
||||||
|
startTime <- liftIO getCurrentTime
|
||||||
|
opkey <-
|
||||||
|
liftIO $
|
||||||
|
saveOperation pool $
|
||||||
|
Operation
|
||||||
|
(ZenithUuid opid)
|
||||||
|
startTime
|
||||||
|
Nothing
|
||||||
|
Processing
|
||||||
|
Nothing
|
||||||
|
case opkey of
|
||||||
|
Nothing ->
|
||||||
|
return $
|
||||||
|
ErrorResponse (callId req) (-32010) "Internal Error"
|
||||||
|
Just opkey' -> do
|
||||||
|
acc <-
|
||||||
|
liftIO $ getAccountById pool $ toSqlKey $ fromIntegral i
|
||||||
|
case acc of
|
||||||
|
Just acc' -> do
|
||||||
|
bl <-
|
||||||
|
liftIO $
|
||||||
|
getLastSyncBlock
|
||||||
|
pool
|
||||||
|
(zcashAccountWalletId $ entityVal acc')
|
||||||
|
_ <-
|
||||||
|
liftIO $
|
||||||
|
forkIO $ do
|
||||||
|
res <-
|
||||||
|
runNoLoggingT $
|
||||||
|
deshieldNotes
|
||||||
|
pool
|
||||||
|
zHost
|
||||||
|
zPort
|
||||||
|
net
|
||||||
|
(entityKey acc')
|
||||||
|
bl
|
||||||
|
k
|
||||||
|
case res of
|
||||||
|
Left e ->
|
||||||
|
finalizeOperation pool opkey' Failed $
|
||||||
|
T.pack $ show e
|
||||||
|
Right rawTx -> do
|
||||||
|
zebraRes <-
|
||||||
|
makeZebraCall
|
||||||
|
zHost
|
||||||
|
zPort
|
||||||
|
"sendrawtransaction"
|
||||||
|
[Data.Aeson.String $ H.toText rawTx]
|
||||||
|
case zebraRes of
|
||||||
|
Left e1 ->
|
||||||
|
finalizeOperation pool opkey' Failed $
|
||||||
|
T.pack $ show e1
|
||||||
|
Right txId ->
|
||||||
|
finalizeOperation pool opkey' Successful $
|
||||||
|
"Tx ID: " <> H.toText txId
|
||||||
|
return $ SendResponse (callId req) opid
|
||||||
|
Nothing ->
|
||||||
|
return $
|
||||||
|
ErrorResponse
|
||||||
|
(callId req)
|
||||||
|
(-32006)
|
||||||
|
"Account does not exist."
|
||||||
|
_anyOtherParams ->
|
||||||
|
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
||||||
|
|
||||||
authenticate :: Config -> BasicAuthCheck Bool
|
authenticate :: Config -> BasicAuthCheck Bool
|
||||||
authenticate config = BasicAuthCheck check
|
authenticate config = BasicAuthCheck check
|
||||||
|
|
|
@ -235,7 +235,7 @@ isValidString c = do
|
||||||
|
|
||||||
padWithZero :: Int -> String -> String
|
padWithZero :: Int -> String -> String
|
||||||
padWithZero n s
|
padWithZero n s
|
||||||
| (length s) >= n = s
|
| length s >= n = s
|
||||||
| otherwise = padWithZero n ("0" ++ s)
|
| otherwise = padWithZero n ("0" ++ s)
|
||||||
|
|
||||||
isEmpty :: [a] -> Bool
|
isEmpty :: [a] -> Bool
|
||||||
|
@ -248,3 +248,6 @@ getChainTip zHost zPort = do
|
||||||
case r of
|
case r of
|
||||||
Left e1 -> pure 0
|
Left e1 -> pure 0
|
||||||
Right i -> pure i
|
Right i -> pure i
|
||||||
|
|
||||||
|
getTransparentFromUA :: UnifiedAddress -> Maybe TransparentAddress
|
||||||
|
getTransparentFromUA ua = TransparentAddress (ua_net ua) <$> t_rec ua
|
||||||
|
|
|
@ -732,8 +732,8 @@
|
||||||
{
|
{
|
||||||
"name": "deshieldfunds",
|
"name": "deshieldfunds",
|
||||||
"summary": "De-shield the given amount of ZEC from the given account",
|
"summary": "De-shield the given amount of ZEC from the given account",
|
||||||
"description": "Creates a new internal transaction with the requested amount of ZEC to the transparent pool.",
|
"description": "Creates a new internal transaction with the requested amount of ZEC to the transparent pool. The fee is not included in the given amount.",
|
||||||
"tags": [{ "$ref": "#/components/tags/draft"}, { "$ref": "#/components/tags/wip"}],
|
"tags": [],
|
||||||
"params": [
|
"params": [
|
||||||
{ "$ref": "#/components/contentDescriptors/AccountId"},
|
{ "$ref": "#/components/contentDescriptors/AccountId"},
|
||||||
{ "$ref": "#/components/contentDescriptors/Amount"}
|
{ "$ref": "#/components/contentDescriptors/Amount"}
|
||||||
|
|
Loading…
Reference in a new issue