RPC Server #103
6 changed files with 129 additions and 6 deletions
|
@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
|
||||
- RPC module
|
||||
- OpenRPC specification
|
||||
- `listwallets` RPC method
|
||||
- `listaccounts` RPC method
|
||||
- `listaddresses` RPC method
|
||||
|
||||
## [0.6.0.0-beta]
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ import Haskoin.Transaction.Common
|
|||
, txHashToHex
|
||||
)
|
||||
import qualified Lens.Micro as ML ((&), (.~), (^.))
|
||||
import ZcashHaskell.Orchard (isValidUnifiedAddress)
|
||||
import ZcashHaskell.Orchard (getSaplingFromUA, isValidUnifiedAddress)
|
||||
import ZcashHaskell.Transparent (encodeTransparentReceiver)
|
||||
import ZcashHaskell.Types
|
||||
( DecodedNote(..)
|
||||
, OrchardAction(..)
|
||||
|
@ -62,7 +63,7 @@ import ZcashHaskell.Types
|
|||
, TransparentBundle(..)
|
||||
, TransparentReceiver(..)
|
||||
, UnifiedAddress(..)
|
||||
, ZcashNet
|
||||
, ZcashNet(..)
|
||||
, decodeHexText
|
||||
)
|
||||
import Zenith.Types
|
||||
|
@ -76,6 +77,7 @@ import Zenith.Types
|
|||
, TransparentSpendingKeyDB
|
||||
, UnifiedAddressDB(..)
|
||||
, ZcashAccountAPI(..)
|
||||
, ZcashAddressAPI(..)
|
||||
, ZcashNetDB(..)
|
||||
, ZcashPool(..)
|
||||
, ZcashWalletAPI(..)
|
||||
|
@ -284,6 +286,27 @@ toZcashAccountAPI a =
|
|||
(fromIntegral $ fromSqlKey $ zcashAccountWalletId $ entityVal a)
|
||||
(zcashAccountName $ entityVal a)
|
||||
|
||||
-- | @WalletAddress@
|
||||
toZcashAddressAPI :: Entity WalletAddress -> ZcashAddressAPI
|
||||
toZcashAddressAPI a =
|
||||
ZcashAddressAPI
|
||||
(fromIntegral $ fromSqlKey $ entityKey a)
|
||||
(fromIntegral $ fromSqlKey $ walletAddressAccId $ entityVal a)
|
||||
(walletAddressName $ entityVal a)
|
||||
(getUA $ walletAddressUAddress $ entityVal a)
|
||||
(getSaplingFromUA $
|
||||
TE.encodeUtf8 $ getUA $ walletAddressUAddress $ entityVal a)
|
||||
(encodeTransparentReceiver
|
||||
(maybe
|
||||
TestNet
|
||||
ua_net
|
||||
((isValidUnifiedAddress .
|
||||
TE.encodeUtf8 . getUA . walletAddressUAddress) $
|
||||
entityVal a)) <$>
|
||||
(t_rec =<<
|
||||
(isValidUnifiedAddress . TE.encodeUtf8 . getUA . walletAddressUAddress)
|
||||
(entityVal a)))
|
||||
|
||||
-- * Database functions
|
||||
-- | Initializes the database
|
||||
initDb ::
|
||||
|
|
|
@ -28,19 +28,27 @@ import ZcashHaskell.Types
|
|||
import Zenith.Core (checkBlockChain, checkZebra)
|
||||
import Zenith.DB
|
||||
( getAccounts
|
||||
, getAddresses
|
||||
, getWallets
|
||||
, initDb
|
||||
, initPool
|
||||
, toZcashAccountAPI
|
||||
, toZcashAddressAPI
|
||||
, toZcashWalletAPI
|
||||
)
|
||||
import Zenith.Types (Config(..), ZcashAccountAPI(..), ZcashWalletAPI(..))
|
||||
import Zenith.Types
|
||||
( Config(..)
|
||||
, ZcashAccountAPI(..)
|
||||
, ZcashAddressAPI(..)
|
||||
, ZcashWalletAPI(..)
|
||||
)
|
||||
import Zenith.Utils (jsonNumber)
|
||||
|
||||
data ZenithMethod
|
||||
= GetInfo
|
||||
| ListWallets
|
||||
| ListAccounts
|
||||
| ListAddresses
|
||||
| UnknownMethod
|
||||
deriving (Eq, Prelude.Show)
|
||||
|
||||
|
@ -48,6 +56,7 @@ instance ToJSON ZenithMethod where
|
|||
toJSON GetInfo = Data.Aeson.String "getinfo"
|
||||
toJSON ListWallets = Data.Aeson.String "listwallets"
|
||||
toJSON ListAccounts = Data.Aeson.String "listaccounts"
|
||||
toJSON ListAddresses = Data.Aeson.String "listaddresses"
|
||||
toJSON UnknownMethod = Data.Aeson.Null
|
||||
|
||||
instance FromJSON ZenithMethod where
|
||||
|
@ -56,12 +65,14 @@ instance FromJSON ZenithMethod where
|
|||
"getinfo" -> pure GetInfo
|
||||
"listwallets" -> pure ListWallets
|
||||
"listaccounts" -> pure ListAccounts
|
||||
"listaddresses" -> pure ListAddresses
|
||||
_ -> pure UnknownMethod
|
||||
|
||||
data ZenithParams
|
||||
= BlankParams
|
||||
| BadParams
|
||||
| AccountsParams !Int
|
||||
| AddressesParams !Int
|
||||
| TestParams !T.Text
|
||||
deriving (Eq, Prelude.Show)
|
||||
|
||||
|
@ -69,12 +80,14 @@ 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 (AddressesParams 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]
|
||||
| AddressListResponse !T.Text ![ZcashAddressAPI]
|
||||
| ErrorResponse !T.Text !Double !T.Text
|
||||
deriving (Eq, Prelude.Show)
|
||||
|
||||
|
@ -85,6 +98,8 @@ instance ToJSON ZenithResponse where
|
|||
object ["jsonrpc" .= ("2.0" :: String), "id" .= i, "result" .= w]
|
||||
toJSON (AccountListResponse i a) =
|
||||
object ["jsonrpc" .= ("2.0" :: String), "id" .= i, "result" .= a]
|
||||
toJSON (AddressListResponse i a) =
|
||||
object ["jsonrpc" .= ("2.0" :: String), "id" .= i, "result" .= a]
|
||||
toJSON (ErrorResponse i c m) =
|
||||
object
|
||||
[ "jsonrpc" .= ("2.0" :: String)
|
||||
|
@ -122,11 +137,17 @@ instance FromJSON ZenithResponse where
|
|||
case V.head n of
|
||||
Object n' -> do
|
||||
v1 <- n' .:? "lastSync"
|
||||
v2 <- n' .:? "wallet"
|
||||
case (v1 :: Maybe Int) of
|
||||
Just _v1' -> do
|
||||
k2 <- parseJSON r1
|
||||
pure $ WalletListResponse i k2
|
||||
Nothing -> fail "Unknown object"
|
||||
Nothing ->
|
||||
case (v2 :: Maybe Int) of
|
||||
Just _v2' -> do
|
||||
k3 <- parseJSON r1
|
||||
pure $ AccountListResponse i k3
|
||||
Nothing -> fail "Unknown object"
|
||||
_anyOther -> fail "Malformed JSON"
|
||||
_anyOther -> fail "Malformed JSON"
|
||||
Just e1 -> pure $ ErrorResponse i (ecode e1) (emessage e1)
|
||||
|
@ -189,6 +210,16 @@ instance FromJSON RpcCall where
|
|||
pure $ RpcCall v i ListAccounts (AccountsParams w)
|
||||
else pure $ RpcCall v i ListAccounts BadParams
|
||||
_anyOther -> pure $ RpcCall v i ListAccounts BadParams
|
||||
ListAddresses -> 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 ListAddresses (AddressesParams x)
|
||||
else pure $ RpcCall v i ListAddresses BadParams
|
||||
_anyOther -> pure $ RpcCall v i ListAddresses BadParams
|
||||
|
||||
type ZenithRPC
|
||||
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
|
||||
|
@ -258,6 +289,26 @@ zenithServer config = getinfo :<|> handleRPC
|
|||
"No accounts available for this wallet. Please create one first"
|
||||
_anyOther ->
|
||||
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
||||
ListAddresses ->
|
||||
case parameters req of
|
||||
AddressesParams a -> do
|
||||
let dbPath = c_dbPath config
|
||||
pool <- liftIO $ runNoLoggingT $ initPool dbPath
|
||||
addrList <-
|
||||
liftIO $
|
||||
runNoLoggingT $ getAddresses pool (toSqlKey $ fromIntegral a)
|
||||
if not (null addrList)
|
||||
then return $
|
||||
AddressListResponse
|
||||
(callId req)
|
||||
(map toZcashAddressAPI addrList)
|
||||
else return $
|
||||
ErrorResponse
|
||||
(callId req)
|
||||
(-32003)
|
||||
"No addresses available for this account. Please create one first"
|
||||
_anyOther ->
|
||||
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
||||
GetInfo ->
|
||||
case parameters req of
|
||||
BlankParams -> do
|
||||
|
|
|
@ -124,6 +124,35 @@ main = do
|
|||
"zh"
|
||||
(-32002)
|
||||
"No accounts available for this wallet. Please create one first"
|
||||
describe "Addresses" $ do
|
||||
describe "listaddresses" $ do
|
||||
it "bad credentials" $ do
|
||||
res <-
|
||||
makeZenithCall
|
||||
"127.0.0.1"
|
||||
nodePort
|
||||
"baduser"
|
||||
"idontknow"
|
||||
ListAddresses
|
||||
BlankParams
|
||||
res `shouldBe` Left "Invalid credentials"
|
||||
it "correct credentials, no addresses" $ do
|
||||
res <-
|
||||
makeZenithCall
|
||||
"127.0.0.1"
|
||||
nodePort
|
||||
nodeUser
|
||||
nodePwd
|
||||
ListAddresses
|
||||
(AddressesParams 1)
|
||||
case res of
|
||||
Left e -> assertFailure e
|
||||
Right r ->
|
||||
r `shouldBe`
|
||||
ErrorResponse
|
||||
"zh"
|
||||
(-32003)
|
||||
"No addresses available for this account. Please create one first"
|
||||
|
||||
startAPI :: Config -> IO ()
|
||||
startAPI config = do
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
"name": "listaddresses",
|
||||
"summary": "List existing addresses for an account ID",
|
||||
"description": "List existing addresses for the given account ID or provide an error if none",
|
||||
"tags": [{ "$ref": "#/components/tags/draft"} ],
|
||||
"tags": [],
|
||||
"result": {
|
||||
"name": "Addresses",
|
||||
"schema": {
|
||||
|
@ -179,6 +179,22 @@
|
|||
"result": {
|
||||
"name": "ListAddresses result",
|
||||
"value": [
|
||||
{
|
||||
"index": 3,
|
||||
"account": 1,
|
||||
"name": "Clothes",
|
||||
"ua": "utest13dq4u4dnf3yddw8lq2n6zdclshra6xsp8zgkc5ydyu6k20zrsscmuex46qa4vh84rgd78sqnlleapznnz7mnzx9wv0unts8pv32paj8se5ca3kves2u4a89uy6e8cf4hnarxydxh7hq2e9uu39punfmm53k5h45xn9k3dx35la8j7munh9td7774m8gkqgc4mn40t69w20uu2gtks7a",
|
||||
"legacy": "ztestsapling188csdsvhdny25am8ume03qr2026hdy03zpg5pq7jmmfxtxtct0e93p0rg80yfxvynqd4gwlwft5",
|
||||
"transparent": "tmMouLwVfRYrF91fWjDJToivmsTWBhxfX4E"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"account": 1,
|
||||
"name": "Vacation",
|
||||
"ua": "utest1hhggl4nxfdx63evps6r7qz50cgacgtdpt9k7dl0734w63zn5qmrp6c2xdv9rkqyfkj6kgau4kz48xtm80e67l534qp02teqq86zuzetxql6z5v32yglg9n2un5zsu0hwcvaunzdfg5qnry6syh2dh9x8eu27de03j9pjfvrqda6acgtc6f0emdfh6r5jvfanmjml4ms5wwj9wfqmamq",
|
||||
"legacy": "ztestsapling1mpup3xv2k9clxaf9wjcr0dt5gnmkprz9s9qsn298mqs356pf39wmh30q3pgsp0w5vyrmj6mrzw2",
|
||||
"transparent": "tmX8qCB96Dq49YZkww3bSty7eZDA4Fq6F4R"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
cabal-version: 3.0
|
||||
name: zenith
|
||||
version: 0.6.0.0-beta
|
||||
version: 0.7.0.0-beta
|
||||
license: MIT
|
||||
license-file: LICENSE
|
||||
author: Rene Vergara
|
||||
|
|
Loading…
Reference in a new issue