Milestone 3: RPC server, ZIP-320 #104
5 changed files with 120 additions and 8 deletions
|
@ -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 ::
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Reference in a new issue