RPC Server #103

Merged
pitmutt merged 129 commits from rav001 into milestone3 2024-11-21 15:30:22 +00:00
6 changed files with 133 additions and 7 deletions
Showing only changes of commit 934bff1454 - Show all commits

View file

@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `listwallets` RPC method - `listwallets` RPC method
- `listaccounts` RPC method - `listaccounts` RPC method
- `listaddresses` RPC method - `listaddresses` RPC method
- `listreceived` RPC method
- `getbalance` RPC method
### Changed ### Changed

View file

@ -72,7 +72,8 @@ import ZcashHaskell.Types
, ZcashNet(..) , ZcashNet(..)
) )
import Zenith.Types import Zenith.Types
( HexStringDB(..) ( AccountBalance(..)
, HexStringDB(..)
, OrchardSpendingKeyDB(..) , OrchardSpendingKeyDB(..)
, PhraseDB(..) , PhraseDB(..)
, RseedDB(..) , RseedDB(..)
@ -1693,6 +1694,32 @@ getUnconfirmedBalance pool za = do
let oBal = sum oAmts let oBal = sum oAmts
return . fromIntegral $ tBal + sBal + oBal return . fromIntegral $ tBal + sBal + oBal
getPoolBalance :: ConnectionPool -> ZcashAccountId -> IO AccountBalance
getPoolBalance pool za = do
trNotes <- getWalletUnspentTrNotes pool za
let tAmts = map (walletTrNoteValue . entityVal) trNotes
let tBal = sum tAmts
sapNotes <- getWalletUnspentSapNotes pool za
let sAmts = map (walletSapNoteValue . entityVal) sapNotes
let sBal = sum sAmts
orchNotes <- getWalletUnspentOrchNotes pool za
let oAmts = map (walletOrchNoteValue . entityVal) orchNotes
let oBal = sum oAmts
return $ AccountBalance tBal sBal oBal
getUnconfPoolBalance :: ConnectionPool -> ZcashAccountId -> IO AccountBalance
getUnconfPoolBalance pool za = do
trNotes <- getWalletUnspentUnconfirmedTrNotes pool za
let tAmts = map (walletTrNoteValue . entityVal) trNotes
let tBal = sum tAmts
sapNotes <- getWalletUnspentUnconfirmedSapNotes pool za
let sAmts = map (walletSapNoteValue . entityVal) sapNotes
let sBal = sum sAmts
orchNotes <- getWalletUnspentUnconfirmedOrchNotes pool za
let oAmts = map (walletOrchNoteValue . entityVal) orchNotes
let oBal = sum oAmts
return $ AccountBalance tBal sBal oBal
clearWalletTransactions :: ConnectionPool -> IO () clearWalletTransactions :: ConnectionPool -> IO ()
clearWalletTransactions pool = do clearWalletTransactions pool = do
runNoLoggingT $ runNoLoggingT $

View file

@ -19,13 +19,12 @@ import Data.Int
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 qualified Data.Vector as V import qualified Data.Vector as V
import Database.Esqueleto.Experimental (toSqlKey) import Database.Esqueleto.Experimental (entityKey, toSqlKey)
import Servant import Servant
import Text.Read (readMaybe) import Text.Read (readMaybe)
import ZcashHaskell.Orchard (parseAddress) import ZcashHaskell.Orchard (parseAddress)
import ZcashHaskell.Types import ZcashHaskell.Types
( RpcError(..) ( RpcError(..)
, ValidAddress(..)
, ZcashNet(..) , ZcashNet(..)
, ZebraGetBlockChainInfo(..) , ZebraGetBlockChainInfo(..)
, ZebraGetInfo(..) , ZebraGetInfo(..)
@ -33,10 +32,13 @@ import ZcashHaskell.Types
import Zenith.Core (checkBlockChain, checkZebra) import Zenith.Core (checkBlockChain, checkZebra)
import Zenith.DB import Zenith.DB
( findNotesByAddress ( findNotesByAddress
, getAccountById
, getAccounts , getAccounts
, getAddressById , getAddressById
, getAddresses , getAddresses
, getExternalAddresses , getExternalAddresses
, getPoolBalance
, getUnconfPoolBalance
, getWalletNotes , getWalletNotes
, getWallets , getWallets
, initPool , initPool
@ -45,7 +47,8 @@ import Zenith.DB
, toZcashWalletAPI , toZcashWalletAPI
) )
import Zenith.Types import Zenith.Types
( Config(..) ( AccountBalance(..)
, Config(..)
, ZcashAccountAPI(..) , ZcashAccountAPI(..)
, ZcashAddressAPI(..) , ZcashAddressAPI(..)
, ZcashNoteAPI(..) , ZcashNoteAPI(..)
@ -59,6 +62,7 @@ data ZenithMethod
| ListAccounts | ListAccounts
| ListAddresses | ListAddresses
| ListReceived | ListReceived
| GetBalance
| UnknownMethod | UnknownMethod
deriving (Eq, Prelude.Show) deriving (Eq, Prelude.Show)
@ -68,6 +72,7 @@ instance ToJSON ZenithMethod where
toJSON ListAccounts = Data.Aeson.String "listaccounts" toJSON ListAccounts = Data.Aeson.String "listaccounts"
toJSON ListAddresses = Data.Aeson.String "listaddresses" toJSON ListAddresses = Data.Aeson.String "listaddresses"
toJSON ListReceived = Data.Aeson.String "listreceived" toJSON ListReceived = Data.Aeson.String "listreceived"
toJSON GetBalance = Data.Aeson.String "getbalance"
toJSON UnknownMethod = Data.Aeson.Null toJSON UnknownMethod = Data.Aeson.Null
instance FromJSON ZenithMethod where instance FromJSON ZenithMethod where
@ -78,6 +83,7 @@ instance FromJSON ZenithMethod where
"listaccounts" -> pure ListAccounts "listaccounts" -> pure ListAccounts
"listaddresses" -> pure ListAddresses "listaddresses" -> pure ListAddresses
"listreceived" -> pure ListReceived "listreceived" -> pure ListReceived
"getbalance" -> pure GetBalance
_ -> pure UnknownMethod _ -> pure UnknownMethod
data ZenithParams data ZenithParams
@ -86,6 +92,7 @@ data ZenithParams
| AccountsParams !Int | AccountsParams !Int
| AddressesParams !Int | AddressesParams !Int
| NotesParams !T.Text | NotesParams !T.Text
| BalanceParams !Int64
| TestParams !T.Text | TestParams !T.Text
deriving (Eq, Prelude.Show) deriving (Eq, Prelude.Show)
@ -96,6 +103,8 @@ instance ToJSON ZenithParams where
toJSON (AddressesParams n) = Data.Aeson.Array $ V.fromList [jsonNumber n] toJSON (AddressesParams n) = Data.Aeson.Array $ V.fromList [jsonNumber n]
toJSON (TestParams t) = Data.Aeson.Array $ V.fromList [Data.Aeson.String t] toJSON (TestParams t) = Data.Aeson.Array $ V.fromList [Data.Aeson.String t]
toJSON (NotesParams t) = Data.Aeson.Array $ V.fromList [Data.Aeson.String t] toJSON (NotesParams t) = Data.Aeson.Array $ V.fromList [Data.Aeson.String t]
toJSON (BalanceParams n) =
Data.Aeson.Array $ V.fromList [jsonNumber $ fromIntegral n]
data ZenithResponse data ZenithResponse
= InfoResponse !T.Text !ZenithInfo = InfoResponse !T.Text !ZenithInfo
@ -103,6 +112,7 @@ data ZenithResponse
| AccountListResponse !T.Text ![ZcashAccountAPI] | AccountListResponse !T.Text ![ZcashAccountAPI]
| AddressListResponse !T.Text ![ZcashAddressAPI] | AddressListResponse !T.Text ![ZcashAddressAPI]
| NoteListResponse !T.Text ![ZcashNoteAPI] | NoteListResponse !T.Text ![ZcashNoteAPI]
| BalanceResponse !T.Text !AccountBalance !AccountBalance
| ErrorResponse !T.Text !Double !T.Text | ErrorResponse !T.Text !Double !T.Text
deriving (Eq, Prelude.Show) deriving (Eq, Prelude.Show)
@ -118,6 +128,8 @@ instance ToJSON ZenithResponse where
, "id" .= i , "id" .= i
, "error" .= object ["code" .= c, "message" .= m] , "error" .= object ["code" .= c, "message" .= m]
] ]
toJSON (BalanceResponse i c u) =
packRpcResponse i $ object ["confirmed" .= c, "unconfirmed" .= u]
instance FromJSON ZenithResponse where instance FromJSON ZenithResponse where
parseJSON = parseJSON =
@ -137,11 +149,19 @@ instance FromJSON ZenithResponse where
case r1 of case r1 of
Object k -> do Object k -> do
v <- k .:? "version" v <- k .:? "version"
v5 <- k .:? "unconfirmed"
case (v :: Maybe String) of case (v :: Maybe String) of
Nothing -> fail "Unknown result"
Just _v' -> do Just _v' -> do
k1 <- parseJSON r1 k1 <- parseJSON r1
pure $ InfoResponse i k1 pure $ InfoResponse i k1
Nothing ->
case (v5 :: Maybe AccountBalance) of
Just _v5' -> do
k6 <- parseJSON r1
j1 <- k6 .: "confirmed"
j2 <- k6 .: "unconfirmed"
pure $ BalanceResponse i j1 j2
Nothing -> fail "Unknown object"
Array n -> do Array n -> do
if V.null n if V.null n
then fail "Malformed JSON" then fail "Malformed JSON"
@ -151,6 +171,7 @@ instance FromJSON ZenithResponse where
v1 <- n' .:? "lastSync" v1 <- n' .:? "lastSync"
v2 <- n' .:? "wallet" v2 <- n' .:? "wallet"
v3 <- n' .:? "ua" v3 <- n' .:? "ua"
v4 <- n' .:? "amountZats"
case (v1 :: Maybe Int) of case (v1 :: Maybe Int) of
Just _v1' -> do Just _v1' -> do
k2 <- parseJSON r1 k2 <- parseJSON r1
@ -165,6 +186,11 @@ instance FromJSON ZenithResponse where
Just _v3' -> do Just _v3' -> do
k4 <- parseJSON r1 k4 <- parseJSON r1
pure $ AddressListResponse i k4 pure $ AddressListResponse i k4
Nothing ->
case (v4 :: Maybe Int) of
Just _v4' -> do
k5 <- parseJSON r1
pure $ NoteListResponse i k5
Nothing -> fail "Unknown object" Nothing -> fail "Unknown object"
_anyOther -> fail "Malformed JSON" _anyOther -> fail "Malformed JSON"
_anyOther -> fail "Malformed JSON" _anyOther -> fail "Malformed JSON"
@ -248,6 +274,16 @@ instance FromJSON RpcCall where
pure $ RpcCall v i ListReceived (NotesParams x) pure $ RpcCall v i ListReceived (NotesParams x)
else pure $ RpcCall v i ListReceived BadParams else pure $ RpcCall v i ListReceived BadParams
_anyOther -> pure $ RpcCall v i ListReceived BadParams _anyOther -> pure $ RpcCall v i ListReceived BadParams
GetBalance -> do
p <- obj .: "params"
case p of
Array a ->
if V.length a == 1
then do
x <- parseJSON $ V.head a
pure $ RpcCall v i GetBalance (BalanceParams x)
else pure $ RpcCall v i GetBalance BadParams
_anyOther -> pure $ RpcCall v i GetBalance BadParams
type ZenithRPC type ZenithRPC
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody = "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
@ -400,6 +436,22 @@ zenithServer config = getinfo :<|> handleRPC
return $ NoteListResponse (callId req) nList return $ NoteListResponse (callId req) nList
_anyOtherParams -> _anyOtherParams ->
return $ ErrorResponse (callId req) (-32602) "Invalid params" return $ ErrorResponse (callId req) (-32602) "Invalid params"
GetBalance ->
case parameters req of
BalanceParams i -> do
let dbPath = c_dbPath config
pool <- liftIO $ runNoLoggingT $ initPool dbPath
acc <- liftIO $ getAccountById pool $ toSqlKey i
case acc of
Just acc' -> do
c <- liftIO $ getPoolBalance pool $ entityKey acc'
u <- liftIO $ getUnconfPoolBalance pool $ entityKey acc'
return $ BalanceResponse (callId req) c u
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

View file

@ -175,6 +175,14 @@ data ZcashNoteAPI = ZcashNoteAPI
$(deriveJSON defaultOptions {fieldLabelModifier = drop 3} ''ZcashNoteAPI) $(deriveJSON defaultOptions {fieldLabelModifier = drop 3} ''ZcashNoteAPI)
data AccountBalance = AccountBalance
{ acb_transparent :: !Int64
, acb_sapling :: !Int64
, acb_orchard :: !Int64
} deriving (Eq, Prelude.Show)
$(deriveJSON defaultOptions {fieldLabelModifier = drop 4} ''AccountBalance)
-- ** `zebrad` -- ** `zebrad`
-- | Type for modeling the tree state response -- | Type for modeling the tree state response
data ZebraTreeInfo = ZebraTreeInfo data ZebraTreeInfo = ZebraTreeInfo

View file

@ -190,6 +190,43 @@ main = do
case res of case res of
Left e -> assertFailure e Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32004) Right (ErrorResponse i c m) -> c `shouldBe` (-32004)
describe "Balance" $ do
describe "getbalance" $ do
it "bad credentials" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
"baduser"
"idontknow"
GetBalance
BlankParams
res `shouldBe` Left "Invalid credentials"
describe "correct credentials" $ do
it "no parameters" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetBalance
BlankParams
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
it "unknown index" $ do
res <-
makeZenithCall
"127.0.0.1"
nodePort
nodeUser
nodePwd
GetBalance
(BalanceParams 17)
case res of
Left e -> assertFailure e
Right (ErrorResponse i c m) -> c `shouldBe` (-32006)
startAPI :: Config -> IO () startAPI :: Config -> IO ()
startAPI config = do startAPI config = do

View file

@ -247,7 +247,7 @@
"name": "getbalance", "name": "getbalance",
"summary": "Get the balance of the given account", "summary": "Get the balance of the given account",
"description": "Get the balance of the given account, including any unconfirmed balance.", "description": "Get the balance of the given account, including any unconfirmed balance.",
"tags": [{"$ref": "#/components/tags/draft"},{"$ref": "#/components/tags/wip"}], "tags": [],
"params": [{ "$ref": "#/components/contentDescriptors/AccountId"}], "params": [{ "$ref": "#/components/contentDescriptors/AccountId"}],
"result": { "result": {
"name": "Balance", "name": "Balance",