RPC Server #103

Merged
pitmutt merged 129 commits from rav001 into milestone3 2024-11-21 15:30:22 +00:00
5 changed files with 120 additions and 8 deletions
Showing only changes of commit 4553f964f3 - Show all commits

View file

@ -75,6 +75,7 @@ import Zenith.Types
, ScopeDB(..)
, TransparentSpendingKeyDB
, UnifiedAddressDB(..)
, ZcashAccountAPI(..)
, ZcashNetDB(..)
, ZcashPool(..)
, ZcashWalletAPI(..)
@ -275,6 +276,14 @@ toZcashWalletAPI w =
(zcashWalletBirthdayHeight $ entityVal w)
(zcashWalletLastSync $ entityVal w)
-- | @ZcashAccount@
toZcashAccountAPI :: Entity ZcashAccount -> ZcashAccountAPI
toZcashAccountAPI a =
ZcashAccountAPI
(fromIntegral $ fromSqlKey $ entityKey a)
(fromIntegral $ fromSqlKey $ zcashAccountWalletId $ entityVal a)
(zcashAccountName $ entityVal a)
-- * Database functions
-- | Initializes the database
initDb ::

View file

@ -17,6 +17,7 @@ import Control.Monad.Logger (runNoLoggingT)
import Data.Aeson
import qualified Data.Text as T
import qualified Data.Vector as V
import Database.Esqueleto.Experimental (toSqlKey)
import Servant
import ZcashHaskell.Types
( RpcError(..)
@ -25,18 +26,28 @@ import ZcashHaskell.Types
, ZebraGetInfo(..)
)
import Zenith.Core (checkBlockChain, checkZebra)
import Zenith.DB (getWallets, initDb, initPool, toZcashWalletAPI)
import Zenith.Types (Config(..), ZcashWalletAPI(..))
import Zenith.DB
( getAccounts
, getWallets
, initDb
, initPool
, toZcashAccountAPI
, toZcashWalletAPI
)
import Zenith.Types (Config(..), ZcashAccountAPI(..), ZcashWalletAPI(..))
import Zenith.Utils (jsonNumber)
data ZenithMethod
= GetInfo
| ListWallets
| ListAccounts
| UnknownMethod
deriving (Eq, Prelude.Show)
instance ToJSON ZenithMethod where
toJSON GetInfo = Data.Aeson.String "getinfo"
toJSON ListWallets = Data.Aeson.String "listwallets"
toJSON ListAccounts = Data.Aeson.String "listaccounts"
toJSON UnknownMethod = Data.Aeson.Null
instance FromJSON ZenithMethod where
@ -44,22 +55,26 @@ instance FromJSON ZenithMethod where
withText "ZenithMethod" $ \case
"getinfo" -> pure GetInfo
"listwallets" -> pure ListWallets
"listaccounts" -> pure ListAccounts
_ -> pure UnknownMethod
data ZenithParams
= BlankParams
| BadParams
| AccountsParams !Int
| TestParams !T.Text
deriving (Eq, Prelude.Show)
instance ToJSON ZenithParams where
toJSON BlankParams = Data.Aeson.Array V.empty
toJSON BadParams = Data.Aeson.Null
toJSON (AccountsParams n) = Data.Aeson.Array $ V.fromList [jsonNumber n]
toJSON (TestParams t) = Data.Aeson.Array $ V.fromList [Data.Aeson.String t]
data ZenithResponse
= InfoResponse !T.Text !ZenithInfo
| WalletListResponse !T.Text ![ZcashWalletAPI]
| AccountListResponse !T.Text ![ZcashAccountAPI]
| ErrorResponse !T.Text !Double !T.Text
deriving (Eq, Prelude.Show)
@ -68,6 +83,8 @@ instance ToJSON ZenithResponse where
object ["jsonrpc" .= ("2.0" :: String), "id" .= t, "result" .= i]
toJSON (WalletListResponse i w) =
object ["jsonrpc" .= ("2.0" :: String), "id" .= i, "result" .= w]
toJSON (AccountListResponse i a) =
object ["jsonrpc" .= ("2.0" :: String), "id" .= i, "result" .= a]
toJSON (ErrorResponse i c m) =
object
[ "jsonrpc" .= ("2.0" :: String)
@ -162,6 +179,16 @@ instance FromJSON RpcCall where
if null (p :: [Value])
then pure $ RpcCall v i GetInfo BlankParams
else pure $ RpcCall v i GetInfo BadParams
ListAccounts -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 1
then do
w <- parseJSON $ V.head a
pure $ RpcCall v i ListAccounts (AccountsParams w)
else pure $ RpcCall v i ListAccounts BadParams
_anyOther -> pure $ RpcCall v i ListAccounts BadParams
type ZenithRPC
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
@ -211,6 +238,26 @@ zenithServer config = getinfo :<|> handleRPC
"No wallets available. Please create one first"
_anyOther ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
ListAccounts ->
case parameters req of
AccountsParams w -> do
let dbPath = c_dbPath config
pool <- liftIO $ runNoLoggingT $ initPool dbPath
accList <-
liftIO $
runNoLoggingT $ getAccounts pool (toSqlKey $ fromIntegral w)
if not (null accList)
then return $
AccountListResponse
(callId req)
(map toZcashAccountAPI accList)
else return $
ErrorResponse
(callId req)
(-32002)
"No accounts available for this wallet. Please create one first"
_anyOther ->
return $ ErrorResponse (callId req) (-32602) "Invalid params"
GetInfo ->
case parameters req of
BlankParams -> do

View file

@ -130,6 +130,24 @@ instance FromJSON ZcashWalletAPI where
l <- obj .: "lastSync"
pure $ ZcashWalletAPI i n net b l
data ZcashAccountAPI = ZcashAccountAPI
{ za_index :: !Int
, za_wallet :: !Int
, za_name :: !T.Text
} deriving (Eq, Prelude.Show)
instance ToJSON ZcashAccountAPI where
toJSON (ZcashAccountAPI i w n) =
object ["index" .= i, "wallet" .= w, "name" .= n]
instance FromJSON ZcashAccountAPI where
parseJSON =
withObject "ZcashAccountAPI" $ \obj -> do
i <- obj .: "index"
w <- obj .: "wallet"
n <- obj .: "name"
pure $ ZcashAccountAPI i w n
-- ** `zebrad`
-- | Type for modeling the tree state response
data ZebraTreeInfo = ZebraTreeInfo

View file

@ -95,6 +95,35 @@ main = do
"zh"
(-32001)
"No wallets available. Please create one first"
describe "Accounts" $ do
describe "listaccounts" $ do
it "bad credentials" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
"baduser"
"idontknow"
ListAccounts
BlankParams
res `shouldBe` Left "Invalid credentials"
it "correct credentials, no accounts" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
ListAccounts
(AccountsParams 1)
case res of
Left e -> assertFailure e
Right r ->
r `shouldBe`
ErrorResponse
"zh"
(-32002)
"No accounts available for this wallet. Please create one first"
startAPI :: Config -> IO ()
startAPI config = do

View file

@ -21,7 +21,7 @@
"name": "getinfo",
"summary": "Get basic Zenith information",
"description": "Get basic information about Zenith, such as the network it is running on and the version of Zebra it is connected to",
"tags": [ { "$ref": "#/components/tags/information" }],
"tags": [],
"result" : {
"name": "Zenith information",
"schema": { "$ref": "#/components/schemas/ZenithInfo" }
@ -52,7 +52,7 @@
"name": "listwallets",
"summary": "Get the list of available wallets",
"description": "Returns a list of available wallets per the network that the Zebra node is running on.",
"tags": [ { "$ref": "#/components/tags/wallet" }],
"tags": [],
"result": {
"name": "Wallets",
"schema": {
@ -98,9 +98,9 @@
},
{
"name": "listaccounts",
"summary": "DRAFT: List existing accounts for a wallet ID",
"summary": "List existing accounts for a wallet ID",
"description": "List existing accounts for the given wallet ID or provide an error if none",
"tags": [ { "$ref": "#/components/tags/wallet" }],
"tags": [],
"result": {
"name": "Accounts",
"schema": {
@ -127,6 +127,16 @@
"result": {
"name": "ListAccounts result",
"value": [
{
"index": 3,
"name": "Business",
"wallet": 1
},
{
"index": 1,
"name": "Savings",
"wallet": 1
}
]
}
@ -180,8 +190,7 @@
},
"examples": {},
"tags": {
"information": {"name": "Information"},
"wallet": {"name": "Wallet"}
"draft": {"name": "Draft"}
},
"errors": {
"ZebraNotAvailable": {