RPC: Shield and de-shield funds (#110)
This PR contains the functionality for the RPC to shield and de-shield funds. Co-authored-by: Rene Vergara A. <rvergara59@protonmail.com> Reviewed-on: #110 Co-authored-by: Rene Vergara <rene@vergara.network> Co-committed-by: Rene Vergara <rene@vergara.network>
This commit is contained in:
parent
15e120bb14
commit
83fa73bc0a
15 changed files with 568 additions and 187 deletions
|
@ -67,7 +67,7 @@ main = do
|
|||
zebraPort
|
||||
(zgb_net chainInfo)
|
||||
threadDelay 90000000
|
||||
putStrLn "Zenith RPC Server 0.7.0.0-beta"
|
||||
putStrLn "Zenith RPC Server 0.8.0.0-beta"
|
||||
putStrLn "------------------------------"
|
||||
putStrLn $
|
||||
"Connected to " ++
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -79,6 +79,7 @@ import Data.Scientific (Scientific, scientific)
|
|||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
|
||||
import qualified Data.UUID as U
|
||||
import qualified Data.Vector as Vec
|
||||
import Database.Persist
|
||||
import Database.Persist.Sqlite
|
||||
|
@ -116,6 +117,7 @@ import Zenith.Types
|
|||
, ValidAddressAPI(..)
|
||||
, ZcashNetDB(..)
|
||||
, ZenithStatus(..)
|
||||
, ZenithUuid(..)
|
||||
)
|
||||
import Zenith.Utils
|
||||
( displayTaz
|
||||
|
@ -778,8 +780,8 @@ listDrawTx znet sel tx =
|
|||
else displayTaz amt
|
||||
fmtAmt =
|
||||
if amt > 0
|
||||
then "↘" <> dispAmount <> " "
|
||||
else " " <> dispAmount <> "↗"
|
||||
then "↘ " <> dispAmount <> " "
|
||||
else " " <> dispAmount <> "↗ "
|
||||
selStr s =
|
||||
if sel
|
||||
then withAttr customAttr (txt $ "> " <> s)
|
||||
|
@ -1283,45 +1285,23 @@ appEvent (BT.VtyEvent e) = do
|
|||
Just (_j, w1) -> return w1
|
||||
Just (_k, w) -> return w
|
||||
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 <-
|
||||
liftIO $
|
||||
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 $
|
||||
forkIO $
|
||||
deshieldTransaction
|
||||
pool
|
||||
(s ^. eventDispatch)
|
||||
(s ^. zebraHost)
|
||||
(s ^. zebraPort)
|
||||
(s ^. network)
|
||||
(entityKey selAcc)
|
||||
bl
|
||||
(ProposedNote
|
||||
(ValidAddressAPI tAddr)
|
||||
(fs1 ^. shAmt)
|
||||
Nothing)
|
||||
BT.modify $ set displayBox SendDisplay
|
||||
BT.modify $ set dialogBox Blank
|
||||
_ <-
|
||||
liftIO $
|
||||
forkIO $
|
||||
deshieldTransaction
|
||||
pool
|
||||
(s ^. eventDispatch)
|
||||
(s ^. zebraHost)
|
||||
(s ^. zebraPort)
|
||||
(s ^. network)
|
||||
(entityKey selAcc)
|
||||
bl
|
||||
(fs1 ^. shAmt)
|
||||
BT.modify $ set displayBox SendDisplay
|
||||
BT.modify $ set dialogBox Blank
|
||||
else do
|
||||
BT.modify $ set msg "Invalid inputs"
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
|
@ -2050,19 +2030,20 @@ shieldTransaction ::
|
|||
shieldTransaction pool chan zHost zPort znet accId bl = do
|
||||
BC.writeBChan chan $ TickMsg "Preparing shielding transaction..."
|
||||
res <- runNoLoggingT $ shieldTransparentNotes pool zHost zPort znet accId bl
|
||||
forM_ res $ \case
|
||||
Left e -> BC.writeBChan chan $ TickMsg $ show e
|
||||
Right rawTx -> do
|
||||
BC.writeBChan chan $ TickMsg "Transaction ready, sending to Zebra..."
|
||||
resp <-
|
||||
makeZebraCall
|
||||
zHost
|
||||
zPort
|
||||
"sendrawtransaction"
|
||||
[Data.Aeson.String $ toText rawTx]
|
||||
case resp of
|
||||
Left e1 -> BC.writeBChan chan $ TickMsg $ "Zebra error: " ++ show e1
|
||||
Right txId -> BC.writeBChan chan $ TickTx txId
|
||||
ops <-
|
||||
mapM
|
||||
(\case
|
||||
Left e -> return $ T.pack $ show e
|
||||
Right x -> do
|
||||
thisOp <- getOperation pool x
|
||||
case thisOp of
|
||||
Nothing -> return ""
|
||||
Just o ->
|
||||
return $
|
||||
(U.toText . getUuid . operationUuid $ entityVal o) <>
|
||||
": " <> (T.pack . show . operationStatus $ entityVal o))
|
||||
res
|
||||
BC.writeBChan chan $ TickMsg $ T.unpack $ T.intercalate "\n" ops
|
||||
|
||||
deshieldTransaction ::
|
||||
ConnectionPool
|
||||
|
@ -2072,7 +2053,7 @@ deshieldTransaction ::
|
|||
-> ZcashNet
|
||||
-> ZcashAccountId
|
||||
-> Int
|
||||
-> ProposedNote
|
||||
-> Scientific
|
||||
-> IO ()
|
||||
deshieldTransaction pool chan zHost zPort znet accId bl pnote = do
|
||||
BC.writeBChan chan $ TickMsg "Deshielding funds..."
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
-- | Core wallet functionality for Zenith
|
||||
module Zenith.Core where
|
||||
|
||||
import Control.Concurrent (forkIO)
|
||||
import Control.Exception (throwIO, try)
|
||||
import Control.Monad (forM, unless, when)
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
|
@ -25,6 +26,8 @@ import Data.Scientific (Scientific, scientific, toBoundedInteger)
|
|||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import Data.Time
|
||||
import qualified Data.UUID as U
|
||||
import Data.UUID.V4 (nextRandom)
|
||||
import qualified Database.Esqueleto.Experimental as ESQ
|
||||
import Database.Persist
|
||||
import Database.Persist.Sqlite
|
||||
|
@ -41,6 +44,7 @@ import ZcashHaskell.Orchard
|
|||
, getOrchardTreeParts
|
||||
, getOrchardWitness
|
||||
, isValidUnifiedAddress
|
||||
, parseAddress
|
||||
, updateOrchardCommitmentTree
|
||||
, updateOrchardWitness
|
||||
)
|
||||
|
@ -80,7 +84,10 @@ import Zenith.Types
|
|||
, ValidAddressAPI(..)
|
||||
, ZcashNetDB(..)
|
||||
, ZebraTreeInfo(..)
|
||||
, ZenithStatus(..)
|
||||
, ZenithUuid(..)
|
||||
)
|
||||
import Zenith.Utils (getTransparentFromUA)
|
||||
|
||||
-- * Zebra Node interaction
|
||||
-- | Checks the status of the `zebrad` node
|
||||
|
@ -746,14 +753,37 @@ deshieldNotes ::
|
|||
-> ZcashNet
|
||||
-> ZcashAccountId
|
||||
-> Int
|
||||
-> ProposedNote
|
||||
-> Scientific
|
||||
-> NoLoggingT IO (Either TxError HexString)
|
||||
deshieldNotes pool zebraHost zebraPort znet za bh pnote = do
|
||||
bal <- liftIO $ getShieldedBalance pool za
|
||||
let zats = pn_amt pnote * scientific 1 8
|
||||
if fromInteger bal > (scientific 2 4 + zats)
|
||||
then prepareTxV2 pool zebraHost zebraPort znet za bh [pnote] Low
|
||||
else return $ Left InsufficientFunds
|
||||
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)
|
||||
then prepareTxV2
|
||||
pool
|
||||
zebraHost
|
||||
zebraPort
|
||||
znet
|
||||
za
|
||||
bh
|
||||
[ ProposedNote
|
||||
(ValidAddressAPI $ Transparent ta)
|
||||
pnote
|
||||
Nothing
|
||||
]
|
||||
Low
|
||||
else return $ Left InsufficientFunds
|
||||
_anyOther -> return $ Left ZHError
|
||||
|
||||
shieldTransparentNotes ::
|
||||
ConnectionPool
|
||||
|
@ -762,8 +792,8 @@ shieldTransparentNotes ::
|
|||
-> ZcashNet
|
||||
-> ZcashAccountId
|
||||
-> Int
|
||||
-> NoLoggingT IO [Either TxError HexString]
|
||||
shieldTransparentNotes pool zebraHost zebraPort znet za bh = do
|
||||
-> NoLoggingT IO [Either TxError U.UUID]
|
||||
shieldTransparentNotes pool zHost zPort znet za bh = do
|
||||
accRead <- liftIO $ getAccountById pool za
|
||||
logDebugN $ T.pack $ "Target block: " ++ show bh
|
||||
case accRead of
|
||||
|
@ -772,58 +802,94 @@ shieldTransparentNotes pool zebraHost zebraPort znet za bh = do
|
|||
return [Left ZHError]
|
||||
Just acc -> do
|
||||
trNotes' <- liftIO $ getWalletUnspentTrNotes pool za
|
||||
dRecvs <- liftIO $ getReceivers pool trNotes'
|
||||
let fNotes =
|
||||
map
|
||||
(\x ->
|
||||
filter (\y -> walletTrNoteAddress (entityVal y) == x) trNotes')
|
||||
dRecvs
|
||||
sTree <- liftIO $ getSaplingTree pool
|
||||
oTree <- liftIO $ getOrchardTree pool
|
||||
forM fNotes $ \trNotes -> do
|
||||
let noteTotal = getTotalAmount (trNotes, [], [])
|
||||
tSpends <-
|
||||
liftIO $
|
||||
prepTSpends
|
||||
(getTranSK $ zcashAccountTPrivateKey $ entityVal acc)
|
||||
trNotes
|
||||
chgAddr <- getInternalAddresses pool $ entityKey acc
|
||||
let internalUA =
|
||||
getUA $ walletAddressUAddress $ entityVal $ head chgAddr
|
||||
let oRcvr =
|
||||
fromJust $
|
||||
o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA)
|
||||
let dummy =
|
||||
OutgoingNote
|
||||
4
|
||||
(getBytes $ getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
|
||||
(getBytes oRcvr)
|
||||
(fromIntegral $ noteTotal - 500)
|
||||
""
|
||||
True
|
||||
let feeAmt = calculateTxFee (trNotes, [], []) [dummy]
|
||||
let snote =
|
||||
OutgoingNote
|
||||
4
|
||||
(getBytes $ getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
|
||||
(getBytes oRcvr)
|
||||
(fromIntegral $ noteTotal - fromIntegral feeAmt)
|
||||
""
|
||||
True
|
||||
tx <-
|
||||
liftIO $
|
||||
createTransaction
|
||||
(maybe (hexString "00") (getHash . value . fst) sTree)
|
||||
(maybe (hexString "00") (getHash . value . fst) oTree)
|
||||
tSpends
|
||||
[]
|
||||
[]
|
||||
[snote]
|
||||
znet
|
||||
(bh + 3)
|
||||
True
|
||||
logDebugN $ T.pack $ show tx
|
||||
return tx
|
||||
if null trNotes'
|
||||
then return [Left InsufficientFunds]
|
||||
else do
|
||||
dRecvs <- liftIO $ getReceivers pool trNotes'
|
||||
let fNotes =
|
||||
map
|
||||
(\x ->
|
||||
filter
|
||||
(\y -> walletTrNoteAddress (entityVal y) == x)
|
||||
trNotes')
|
||||
dRecvs
|
||||
sTree <- liftIO $ getSaplingTree pool
|
||||
oTree <- liftIO $ getOrchardTree pool
|
||||
forM fNotes $ \trNotes -> do
|
||||
opid <- liftIO nextRandom
|
||||
startTime <- liftIO getCurrentTime
|
||||
opkey <-
|
||||
liftIO $
|
||||
saveOperation pool $
|
||||
Operation (ZenithUuid opid) startTime Nothing Processing Nothing
|
||||
case opkey of
|
||||
Nothing -> return $ Left ZHError
|
||||
Just opkey' -> do
|
||||
let noteTotal = getTotalAmount (trNotes, [], [])
|
||||
tSpends <-
|
||||
liftIO $
|
||||
prepTSpends
|
||||
(getTranSK $ zcashAccountTPrivateKey $ entityVal acc)
|
||||
trNotes
|
||||
chgAddr <- getInternalAddresses pool $ entityKey acc
|
||||
let internalUA =
|
||||
getUA $ walletAddressUAddress $ entityVal $ head chgAddr
|
||||
let oRcvr =
|
||||
fromJust $
|
||||
o_rec =<< isValidUnifiedAddress (E.encodeUtf8 internalUA)
|
||||
let dummy =
|
||||
OutgoingNote
|
||||
4
|
||||
(getBytes $
|
||||
getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
|
||||
(getBytes oRcvr)
|
||||
(fromIntegral $ noteTotal - 500)
|
||||
""
|
||||
True
|
||||
let feeAmt = calculateTxFee (trNotes, [], []) [dummy]
|
||||
let snote =
|
||||
OutgoingNote
|
||||
4
|
||||
(getBytes $
|
||||
getOrchSK $ zcashAccountOrchSpendKey $ entityVal acc)
|
||||
(getBytes oRcvr)
|
||||
(fromIntegral $ noteTotal - fromIntegral feeAmt)
|
||||
""
|
||||
True
|
||||
_ <-
|
||||
liftIO $
|
||||
forkIO $ do
|
||||
tx <-
|
||||
liftIO $
|
||||
createTransaction
|
||||
(maybe (hexString "00") (getHash . value . fst) sTree)
|
||||
(maybe (hexString "00") (getHash . value . fst) oTree)
|
||||
tSpends
|
||||
[]
|
||||
[]
|
||||
[snote]
|
||||
znet
|
||||
(bh + 3)
|
||||
True
|
||||
case tx of
|
||||
Left e ->
|
||||
finalizeOperation pool opkey' Failed $ T.pack $ show e
|
||||
Right rawTx -> do
|
||||
zebraRes <-
|
||||
makeZebraCall
|
||||
zHost
|
||||
zPort
|
||||
"sendrawtransaction"
|
||||
[Data.Aeson.String $ toText rawTx]
|
||||
case zebraRes of
|
||||
Left e1 ->
|
||||
finalizeOperation pool opkey' Failed $
|
||||
T.pack $ show e1
|
||||
Right txId ->
|
||||
finalizeOperation pool opkey' Successful $
|
||||
"Tx ID: " <> toText txId
|
||||
logDebugN $ T.pack $ show opid
|
||||
return $ Right opid
|
||||
where
|
||||
getTotalAmount ::
|
||||
( [Entity WalletTrNote]
|
||||
|
|
|
@ -630,6 +630,7 @@ getAddresses pool a =
|
|||
addrs <- from $ table @WalletAddress
|
||||
where_ (addrs ^. WalletAddressAccId ==. val a)
|
||||
where_ (addrs ^. WalletAddressScope ==. val (ScopeDB External))
|
||||
orderBy [asc $ addrs ^. WalletAddressId]
|
||||
pure addrs
|
||||
|
||||
getAddressById ::
|
||||
|
|
|
@ -28,6 +28,7 @@ import Data.Scientific (Scientific, fromFloatDigits)
|
|||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
|
||||
import qualified Data.UUID as U
|
||||
import Database.Esqueleto.Experimental (ConnectionPool, fromSqlKey)
|
||||
import Database.Persist
|
||||
import Lens.Micro ((&), (+~), (.~), (?~), (^.), set)
|
||||
|
@ -1750,19 +1751,20 @@ shieldTransaction config znet accId sendMsg = do
|
|||
pool <- runNoLoggingT $ initPool dbPath
|
||||
bl <- getChainTip zHost zPort
|
||||
res <- runNoLoggingT $ shieldTransparentNotes pool zHost zPort znet accId bl
|
||||
forM_ res $ \case
|
||||
Left e -> sendMsg $ ShowError $ T.pack (show e)
|
||||
Right rawTx -> do
|
||||
sendMsg $ ShowMsg "Transaction ready, sending to Zebra..."
|
||||
resp <-
|
||||
makeZebraCall
|
||||
zHost
|
||||
zPort
|
||||
"sendrawtransaction"
|
||||
[Data.Aeson.String $ toText rawTx]
|
||||
case resp of
|
||||
Left e1 -> sendMsg $ ShowError $ "Zebra error: " <> T.pack (show e1)
|
||||
Right txId -> sendMsg $ ShowTxId txId
|
||||
ops <-
|
||||
mapM
|
||||
(\case
|
||||
Left e -> return $ T.pack $ show e
|
||||
Right x -> do
|
||||
thisOp <- getOperation pool x
|
||||
case thisOp of
|
||||
Nothing -> return ""
|
||||
Just o ->
|
||||
return $
|
||||
(U.toText . getUuid . operationUuid $ entityVal o) <>
|
||||
": " <> (T.pack . show . operationStatus $ entityVal o))
|
||||
res
|
||||
sendMsg $ ShowMsg $ T.intercalate "\n" ops
|
||||
|
||||
deshieldTransaction ::
|
||||
Config
|
||||
|
@ -1782,40 +1784,20 @@ deshieldTransaction config znet accId addR pnote sendMsg = do
|
|||
let zPort = c_zebraPort config
|
||||
pool <- runNoLoggingT $ initPool dbPath
|
||||
bl <- getChainTip zHost zPort
|
||||
let tAddrMaybe =
|
||||
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
|
||||
res <- runNoLoggingT $ deshieldNotes pool zHost zPort znet accId bl pnote
|
||||
case res of
|
||||
Left e -> sendMsg $ ShowError $ T.pack (show e)
|
||||
Right rawTx -> do
|
||||
sendMsg $ ShowModal "Transaction ready, sending to Zebra..."
|
||||
resp <-
|
||||
makeZebraCall
|
||||
zHost
|
||||
zPort
|
||||
znet
|
||||
accId
|
||||
bl
|
||||
(ProposedNote (ValidAddressAPI tAddr) pnote Nothing)
|
||||
case res of
|
||||
Left e -> sendMsg $ ShowError $ T.pack (show e)
|
||||
Right rawTx -> do
|
||||
sendMsg $ ShowModal "Transaction ready, sending to Zebra..."
|
||||
resp <-
|
||||
makeZebraCall
|
||||
zHost
|
||||
zPort
|
||||
"sendrawtransaction"
|
||||
[Data.Aeson.String $ toText rawTx]
|
||||
case resp of
|
||||
Left e1 -> sendMsg $ ShowError $ "Zebra error: " <> showt e1
|
||||
Right txId -> sendMsg $ ShowTxId txId
|
||||
"sendrawtransaction"
|
||||
[Data.Aeson.String $ toText rawTx]
|
||||
case resp of
|
||||
Left e1 -> sendMsg $ ShowError $ "Zebra error: " <> showt e1
|
||||
Right txId -> sendMsg $ ShowTxId txId
|
||||
|
||||
sendTransaction ::
|
||||
Config
|
||||
|
|
|
@ -20,7 +20,7 @@ import Control.Monad.Logger (runFileLoggingT, runNoLoggingT, runStderrLoggingT)
|
|||
import Data.Aeson
|
||||
import qualified Data.HexString as H
|
||||
import Data.Int
|
||||
import Data.Scientific (floatingOrInteger)
|
||||
import Data.Scientific (Scientific(..), floatingOrInteger)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import Data.Time.Clock (getCurrentTime)
|
||||
|
@ -50,7 +50,9 @@ import Zenith.Core
|
|||
( checkBlockChain
|
||||
, createCustomWalletAddress
|
||||
, createZcashAccount
|
||||
, deshieldNotes
|
||||
, prepareTxV2
|
||||
, shieldTransparentNotes
|
||||
, syncWallet
|
||||
, updateCommitmentTrees
|
||||
)
|
||||
|
@ -121,6 +123,8 @@ data ZenithMethod
|
|||
| GetNewAddress
|
||||
| GetOperationStatus
|
||||
| SendMany
|
||||
| ShieldNotes
|
||||
| DeshieldFunds
|
||||
| UnknownMethod
|
||||
deriving (Eq, Prelude.Show)
|
||||
|
||||
|
@ -136,6 +140,8 @@ instance ToJSON ZenithMethod where
|
|||
toJSON GetNewAddress = Data.Aeson.String "getnewaddress"
|
||||
toJSON GetOperationStatus = Data.Aeson.String "getoperationstatus"
|
||||
toJSON SendMany = Data.Aeson.String "sendmany"
|
||||
toJSON ShieldNotes = Data.Aeson.String "shieldnotes"
|
||||
toJSON DeshieldFunds = Data.Aeson.String "deshieldfunds"
|
||||
toJSON UnknownMethod = Data.Aeson.Null
|
||||
|
||||
instance FromJSON ZenithMethod where
|
||||
|
@ -152,6 +158,8 @@ instance FromJSON ZenithMethod where
|
|||
"getnewaddress" -> pure GetNewAddress
|
||||
"getoperationstatus" -> pure GetOperationStatus
|
||||
"sendmany" -> pure SendMany
|
||||
"shieldnotes" -> pure ShieldNotes
|
||||
"deshieldfunds" -> pure DeshieldFunds
|
||||
_ -> pure UnknownMethod
|
||||
|
||||
data ZenithParams
|
||||
|
@ -167,6 +175,8 @@ data ZenithParams
|
|||
| OpParams !ZenithUuid
|
||||
| SendParams !Int ![ProposedNote] !PrivacyPolicy
|
||||
| TestParams !T.Text
|
||||
| ShieldNotesParams !Int
|
||||
| DeshieldParams !Int !Scientific
|
||||
deriving (Eq, Prelude.Show)
|
||||
|
||||
instance ToJSON ZenithParams where
|
||||
|
@ -191,6 +201,9 @@ instance ToJSON ZenithParams where
|
|||
Data.Aeson.Array $ V.fromList [Data.Aeson.String $ U.toText $ getUuid i]
|
||||
toJSON (SendParams i ns p) =
|
||||
Data.Aeson.Array $ V.fromList [jsonNumber i, toJSON ns, toJSON p]
|
||||
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
|
||||
= InfoResponse !T.Text !ZenithInfo
|
||||
|
@ -203,6 +216,7 @@ data ZenithResponse
|
|||
| NewAddrResponse !T.Text !ZcashAddressAPI
|
||||
| OpResponse !T.Text !Operation
|
||||
| SendResponse !T.Text !U.UUID
|
||||
| MultiOpResponse !T.Text ![T.Text]
|
||||
| ErrorResponse !T.Text !Double !T.Text
|
||||
deriving (Eq, Prelude.Show)
|
||||
|
||||
|
@ -224,6 +238,7 @@ instance ToJSON ZenithResponse where
|
|||
toJSON (NewAddrResponse i a) = packRpcResponse i a
|
||||
toJSON (OpResponse i u) = packRpcResponse i u
|
||||
toJSON (SendResponse i o) = packRpcResponse i o
|
||||
toJSON (MultiOpResponse i o) = packRpcResponse i o
|
||||
|
||||
instance FromJSON ZenithResponse where
|
||||
parseJSON =
|
||||
|
@ -298,6 +313,9 @@ instance FromJSON ZenithResponse where
|
|||
k5 <- parseJSON r1
|
||||
pure $ NoteListResponse i k5
|
||||
Nothing -> fail "Unknown object"
|
||||
String s -> do
|
||||
k7 <- parseJSON r1
|
||||
pure $ MultiOpResponse i k7
|
||||
_anyOther -> fail "Malformed JSON"
|
||||
Number k -> do
|
||||
case floatingOrInteger k of
|
||||
|
@ -489,6 +507,27 @@ instance FromJSON RpcCall where
|
|||
_anyOther -> pure $ RpcCall v i SendMany BadParams
|
||||
else pure $ RpcCall v i SendMany BadParams
|
||||
_anyOther -> pure $ RpcCall v i SendMany BadParams
|
||||
ShieldNotes -> do
|
||||
p <- obj .: "params"
|
||||
case p of
|
||||
Array a ->
|
||||
if V.length a == 1
|
||||
then do
|
||||
x <- parseJSON $ a V.! 0
|
||||
pure $ RpcCall v i ShieldNotes (ShieldNotesParams x)
|
||||
else 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
|
||||
= "status" :> Get '[ JSON] Value :<|> BasicAuth "zenith-realm" Bool :> ReqBody
|
||||
|
@ -511,7 +550,7 @@ zenithServer state = getinfo :<|> handleRPC
|
|||
getinfo =
|
||||
return $
|
||||
object
|
||||
[ "version" .= ("0.7.0.0-beta" :: String)
|
||||
[ "version" .= ("0.8.0.0-beta" :: String)
|
||||
, "network" .= ("testnet" :: String)
|
||||
]
|
||||
handleRPC :: Bool -> RpcCall -> Handler ZenithResponse
|
||||
|
@ -587,7 +626,7 @@ zenithServer state = getinfo :<|> handleRPC
|
|||
return $
|
||||
InfoResponse
|
||||
(callId req)
|
||||
(ZenithInfo "0.7.0.0-beta" (w_network state) (w_build state))
|
||||
(ZenithInfo "0.8.0.0-beta" (w_network state) (w_build state))
|
||||
_anyOtherParams ->
|
||||
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
||||
ListReceived ->
|
||||
|
@ -871,6 +910,137 @@ zenithServer state = getinfo :<|> handleRPC
|
|||
"Account does not exist."
|
||||
_anyOtherParams ->
|
||||
return $ ErrorResponse (callId req) (-32602) "Invalid params"
|
||||
ShieldNotes -> do
|
||||
case parameters req of
|
||||
ShieldNotesParams i -> 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
|
||||
acc <-
|
||||
liftIO $ getAccountById pool $ toSqlKey $ fromIntegral i
|
||||
case acc of
|
||||
Just acc' -> do
|
||||
bl <-
|
||||
liftIO $
|
||||
getLastSyncBlock
|
||||
pool
|
||||
(zcashAccountWalletId $ entityVal acc')
|
||||
opids <-
|
||||
liftIO $
|
||||
runNoLoggingT $
|
||||
shieldTransparentNotes
|
||||
pool
|
||||
zHost
|
||||
zPort
|
||||
net
|
||||
(entityKey acc')
|
||||
bl
|
||||
let ops =
|
||||
map
|
||||
(\case
|
||||
Left e -> T.pack $ show e
|
||||
Right op -> U.toText op)
|
||||
opids
|
||||
return $ MultiOpResponse (callId req) ops
|
||||
Nothing ->
|
||||
return $
|
||||
ErrorResponse
|
||||
(callId req)
|
||||
(-32006)
|
||||
"Account does not exist."
|
||||
_anyOtherParams ->
|
||||
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 check
|
||||
|
|
|
@ -235,7 +235,7 @@ isValidString c = do
|
|||
|
||||
padWithZero :: Int -> String -> String
|
||||
padWithZero n s
|
||||
| (length s) >= n = s
|
||||
| length s >= n = s
|
||||
| otherwise = padWithZero n ("0" ++ s)
|
||||
|
||||
isEmpty :: [a] -> Bool
|
||||
|
@ -248,3 +248,6 @@ getChainTip zHost zPort = do
|
|||
case r of
|
||||
Left e1 -> pure 0
|
||||
Right i -> pure i
|
||||
|
||||
getTransparentFromUA :: UnifiedAddress -> Maybe TransparentAddress
|
||||
getTransparentFromUA ua = TransparentAddress (ua_net ua) <$> t_rec ua
|
||||
|
|
|
@ -86,7 +86,7 @@ main = do
|
|||
Left e -> assertFailure e
|
||||
Right r ->
|
||||
r `shouldBe`
|
||||
InfoResponse "zh" (ZenithInfo "0.7.0.0-beta" TestNet "v1.9.0")
|
||||
InfoResponse "zh" (ZenithInfo "0.8.0.0-beta" TestNet "v2.1.0")
|
||||
describe "Wallets" $ do
|
||||
describe "listwallet" $ do
|
||||
it "bad credentials" $ do
|
||||
|
@ -676,6 +676,54 @@ main = do
|
|||
case res of
|
||||
Left e -> assertFailure e
|
||||
Right (SendResponse i o) -> o `shouldNotBe` U.nil
|
||||
describe "Shield notes" $ do
|
||||
it "bad credentials" $ do
|
||||
res <-
|
||||
makeZenithCall
|
||||
"127.0.0.1"
|
||||
nodePort
|
||||
"baduser"
|
||||
"idontknow"
|
||||
ShieldNotes
|
||||
BlankParams
|
||||
res `shouldBe` Left "Invalid credentials"
|
||||
describe "correct credentials" $ do
|
||||
it "no parameters" $ do
|
||||
res <-
|
||||
makeZenithCall
|
||||
"127.0.0.1"
|
||||
nodePort
|
||||
nodeUser
|
||||
nodePwd
|
||||
ShieldNotes
|
||||
BlankParams
|
||||
case res of
|
||||
Left e -> assertFailure e
|
||||
Right (ErrorResponse i c m) -> c `shouldBe` (-32602)
|
||||
it "invalid account" $ do
|
||||
res <-
|
||||
makeZenithCall
|
||||
"127.0.0.1"
|
||||
nodePort
|
||||
nodeUser
|
||||
nodePwd
|
||||
ShieldNotes
|
||||
(ShieldNotesParams 27)
|
||||
case res of
|
||||
Left e -> assertFailure e
|
||||
Right (ErrorResponse i c m) -> c `shouldBe` (-32006)
|
||||
it "valid account" $ do
|
||||
res <-
|
||||
makeZenithCall
|
||||
"127.0.0.1"
|
||||
nodePort
|
||||
nodeUser
|
||||
nodePwd
|
||||
ShieldNotes
|
||||
(ShieldNotesParams 1)
|
||||
case res of
|
||||
Left e -> assertFailure e
|
||||
Right (MultiOpResponse i c) -> c `shouldNotBe` []
|
||||
|
||||
startAPI :: Config -> IO ()
|
||||
startAPI config = do
|
||||
|
|
31
test/Spec.hs
31
test/Spec.hs
|
@ -643,8 +643,7 @@ main = do
|
|||
case ix of
|
||||
Nothing -> assertFailure "couldn't find index at block"
|
||||
Just i -> do
|
||||
updatedTree <-
|
||||
runFileLoggingT "test.log" $ truncateTree oTree i
|
||||
updatedTree <- runNoLoggingT $ truncateTree oTree i
|
||||
let finalAnchor =
|
||||
getOrchardTreeAnchor $
|
||||
OrchardCommitmentTree $ ztiOrchard zebraTreesIn
|
||||
|
@ -737,7 +736,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -763,7 +762,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -787,7 +786,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -815,7 +814,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -847,7 +846,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -873,7 +872,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -897,7 +896,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -926,7 +925,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -957,7 +956,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -983,7 +982,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -1007,7 +1006,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -1034,7 +1033,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -1061,7 +1060,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
@ -1086,7 +1085,7 @@ main = do
|
|||
Just ua -> do
|
||||
pool <- runNoLoggingT $ initPool "/home/rav/Zenith/zenith.db"
|
||||
tx <-
|
||||
runFileLoggingT "zenith.log" $
|
||||
runNoLoggingT $
|
||||
prepareTxV2
|
||||
pool
|
||||
"localhost"
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4289a9ded67ef2ca432abc412934fb5b8b59a9cf
|
||||
Subproject commit 7d3ae36d2b48b8ed91a70e40a77fb7efe57765a0
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"openrpc": "1.0.0-rc1",
|
||||
"info": {
|
||||
"version": "0.7.0.0-beta",
|
||||
"version": "0.8.0.0-beta",
|
||||
"title": "Zenith RPC",
|
||||
"description": "The RPC methods to interact with the Zenith Zcash wallet",
|
||||
"license": {
|
||||
|
@ -605,10 +605,9 @@
|
|||
],
|
||||
"paramStructure": "by-position",
|
||||
"result": {
|
||||
"name": "Operation ID(s)",
|
||||
"name": "Operation ID",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/contentDescriptors/OperationId"}
|
||||
"$ref": "#/components/contentDescriptors/OperationId"
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
|
@ -620,7 +619,7 @@
|
|||
{
|
||||
"name": "Account index",
|
||||
"summary": "The index for the account to use",
|
||||
"value": "1"
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "Privacy Policy",
|
||||
|
@ -641,9 +640,8 @@
|
|||
],
|
||||
"result": {
|
||||
"name": "SendMany result",
|
||||
"value": [
|
||||
"3cc31c07-07cf-4a6e-9190-156c4b8c4088"
|
||||
]
|
||||
"value": "3cc31c07-07cf-4a6e-9190-156c4b8c4088"
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -669,6 +667,130 @@
|
|||
"errors": [
|
||||
{ "$ref": "#/components/errors/OpNotFound" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "shieldnotes",
|
||||
"summary": "Shield all transparent notes into the Orchard pool for the given account",
|
||||
"description": "Creates one or more transactions, grouping all the unspent transparent notes for the given account by their transparent address to avoid associating different transparent addresses. These notes are sent to the given account's internal change address as shielded Orchard notes.",
|
||||
"tags": [],
|
||||
"params": [
|
||||
{ "$ref": "#/components/contentDescriptors/AccountId"}
|
||||
],
|
||||
"paramStructure": "by-position",
|
||||
"result": {
|
||||
"name": "Operation ID(s)",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/contentDescriptors/OperationId"}
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"name": "Shield transparent notes",
|
||||
"summary": "Shield transparent notes",
|
||||
"description": "Shield the transparent notes in a given account",
|
||||
"params": [
|
||||
{
|
||||
"name": "Account index",
|
||||
"summary": "The index for the account to use",
|
||||
"value": "3"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "ShieldNotes result",
|
||||
"value": [
|
||||
"ab350df0-9f57-44c0-9e0d-f7b8af1f4231",
|
||||
"8c6f2656-22ef-4f9d-b465-80ddd13fc485"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "No transparent funds",
|
||||
"summary": "Shield transparent notes with no transparent funds",
|
||||
"description": "Attempt to shield the transparent notes in a given account, when account has none",
|
||||
"params": [
|
||||
{
|
||||
"name": "Account index",
|
||||
"summary": "The index for the account to use",
|
||||
"value": "3"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "ShieldNotes result",
|
||||
"value": [
|
||||
"InsufficientFunds"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{ "$ref": "#/components/errors/ZebraNotAvailable" },
|
||||
{ "$ref": "#/components/errors/ZenithBusy" },
|
||||
{ "$ref": "#/components/errors/InvalidAccount" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "deshieldfunds",
|
||||
"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. The fee is not included in the given amount.",
|
||||
"tags": [],
|
||||
"params": [
|
||||
{ "$ref": "#/components/contentDescriptors/AccountId"},
|
||||
{ "$ref": "#/components/contentDescriptors/Amount"}
|
||||
],
|
||||
"paramStructure": "by-position",
|
||||
"result": {
|
||||
"name": "Operation ID",
|
||||
"schema": {
|
||||
"$ref": "#/components/contentDescriptors/OperationId"
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"name": "De-Shield funds",
|
||||
"summary": "De-shield funds",
|
||||
"description": "Move the given amount of ZEC for the given acount from the shielded pool to the transparent pool",
|
||||
"params": [
|
||||
{
|
||||
"name": "Account index",
|
||||
"summary": "The index for the account to use",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"name": "Amount",
|
||||
"summary": "The amount of ZEC to use",
|
||||
"value": 1.23
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "Deshield funds result",
|
||||
"value": "ab350df0-9f57-44c0-9e0d-f7b8af1f4231"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "No transparent funds",
|
||||
"summary": "Shield transparent notes with no transparent funds",
|
||||
"description": "Attempt to shield the transparent notes in a given account, when account has none",
|
||||
"params": [
|
||||
{
|
||||
"name": "Account index",
|
||||
"summary": "The index for the account to use",
|
||||
"value": "3"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "ShieldNotes result",
|
||||
"value": [
|
||||
"InsufficientFunds"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{ "$ref": "#/components/errors/ZebraNotAvailable" },
|
||||
{ "$ref": "#/components/errors/ZenithBusy" },
|
||||
{ "$ref": "#/components/errors/InvalidAccount" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
|
@ -700,6 +822,15 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"Amount": {
|
||||
"name": "A numeric amount",
|
||||
"summary": "A numeric amount",
|
||||
"description": "A number that represents an amount to be used by a function as an input",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"Name": {
|
||||
"name": "Name",
|
||||
"summary": "A user-friendly name",
|
||||
|
|
BIN
zenith_er.bmp
BIN
zenith_er.bmp
Binary file not shown.
Before Width: | Height: | Size: 7.7 MiB |
BIN
zenith_er.png
BIN
zenith_er.png
Binary file not shown.
Before Width: | Height: | Size: 329 KiB |
Loading…
Reference in a new issue