Skip to content
Permalink
Browse files

[DEVOPS-1195] initial proof of concept query-balance API

  • Loading branch information...
cleverca22 committed Mar 12, 2019
1 parent 35e8f14 commit d7ff59c2c106c95235a4491156fefadfc94c9629
@@ -18,6 +18,6 @@ main = withCompileInfo $ do
let lArgs = loggingParams "node" cArgs
let nArgs = NodeArgs { behaviorConfigPath = Nothing }

putText "Wallet is starting..."
putText "Wallet is starting...\n"

launchNode nArgs cArgs lArgs (actionWithWallet wArgs)
@@ -0,0 +1,35 @@
let
cardanoPkgs = import ../. {};
cfgFiles = cardanoPkgs.pkgs.runCommand "cfg" {} ''
mkdir $out
cd $out
cp ${../lib/configuration.yaml} configuration.yaml
cp ${../lib/mainnet-genesis.json} mainnet-genesis.json
cp ${../lib/testnet-genesis.json} testnet-genesis.json
cp ${../lib/mainnet-genesis-dryrun-with-stakeholders.json} mainnet-genesis-dryrun-with-stakeholders.json
'';
makeHelper = cfg: cardanoPkgs.pkgs.writeScriptBin "test-wallet-${cfg.name}" ''
#!/bin/sh
set -e
runhaskell Setup.hs build cardano-node
mkdir -pv states/${cfg.name}/tls/{server,client}
STATE=states/${cfg.name}
${cardanoPkgs.cardano-sl-tools}/bin/cardano-x509-certificates --server-out-dir $STATE/tls/server --clients-out-dir $STATE/tls/client --configuration-file ${cfgFiles}/configuration.yaml --configuration-key ${cfg.key}
dist/build/cardano-node/cardano-node --configuration-file ${cfgFiles}/configuration.yaml --configuration-key ${cfg.key} \
--db-path states/${cfg.name}/DB --keyfile states/${cfg.name}/secret.key \
--logs-prefix states/${cfg.name}/logs --topology ${../script-runner/. + "/topology-${cfg.name}.yaml"} \
--tlscert $STATE/tls/server/server.crt --tlskey $STATE/tls/server/server.key \
--tlsca $STATE/tls/server/ca.crt \
--log-console-off
'';
mainnet = makeHelper { name = "mainnet"; key = "mainnet_full"; };
staging = makeHelper { name = "staging"; key = "mainnet_dryrun_full"; };
testnet = makeHelper { name = "testnet"; key = "testnet_full"; };
in cardanoPkgs.cardano-wallet.env.overrideAttrs (drv: {
buildInputs = drv.buildInputs ++ [ mainnet staging testnet ];
})
@@ -9,7 +9,8 @@ import Servant

import Cardano.Wallet.API.Response (APIResponse, ValidJSON)
import Cardano.Wallet.API.Types
import Cardano.Wallet.API.V1.Types (V1, Wallet, WalletImport)
import Cardano.Wallet.API.V1.Types (V1, Wallet, WalletImport, WalletBalance)
import Pos.Crypto.Signing (EncryptedSecretKey)

type API = Tag "Internal" ('TagDescription
"This section contains endpoints so-called 'Internal'. They are only\
@@ -37,4 +38,8 @@ type API = Tag "Internal" ('TagDescription
:> Summary "Import a Wallet from disk."
:> ReqBody '[ValidJSON] WalletImport
:> Post '[ValidJSON] (APIResponse Wallet)
:<|> "query-balance"
:> Summary "query the balance from a pubkey"
:> ReqBody '[ValidJSON] EncryptedSecretKey
:> Post '[ValidJSON] (APIResponse WalletBalance)
)
@@ -8,16 +8,18 @@ import Pos.Chain.Update (SoftwareVersion)

import qualified Cardano.Wallet.API.Internal as Internal
import Cardano.Wallet.API.Response (APIResponse, single)
import Cardano.Wallet.API.V1.Types (V1, Wallet, WalletImport)
import Cardano.Wallet.API.V1.Types (V1, Wallet, WalletImport, WalletBalance)
import Cardano.Wallet.WalletLayer (PassiveWalletLayer)
import qualified Cardano.Wallet.WalletLayer as WalletLayer
import Pos.Crypto.Signing (EncryptedSecretKey)

handlers :: PassiveWalletLayer IO -> ServerT Internal.API Handler
handlers w = nextUpdate w
:<|> applyUpdate w
:<|> postponeUpdate w
:<|> resetWalletState w
:<|> importWallet w
:<|> queryWalletBalance w

nextUpdate :: PassiveWalletLayer IO -> Handler (APIResponse (V1 SoftwareVersion))
nextUpdate w = do
@@ -42,3 +44,8 @@ importWallet w walletImport = do
case res of
Left e -> throwM e
Right importedWallet -> pure $ single importedWallet

queryWalletBalance :: PassiveWalletLayer IO -> EncryptedSecretKey -> Handler (APIResponse WalletBalance)
queryWalletBalance w esk = do
balance <- liftIO (WalletLayer.queryWalletBalance w esk)
pure $ single balance
@@ -68,6 +68,8 @@ module Cardano.Wallet.API.V1.Types (
, EstimatedFees (..)
-- * Updates
, WalletSoftwareUpdate (..)
-- wallet balance
, WalletBalance (..)
-- * Importing a wallet from a backup
, WalletImport (..)
-- * Settings
@@ -162,14 +164,15 @@ import Cardano.Mnemonic (Mnemonic)
import qualified Pos.Chain.Txp as Txp
import qualified Pos.Client.Txp.Util as Core
import qualified Pos.Core as Core
import Pos.Crypto (PublicKey (..), decodeHash, hashHexF)
import Pos.Crypto (PublicKey (..), decodeHash, hashHexF, EncryptedSecretKey)
import qualified Pos.Crypto.Signing as Core
import Pos.Infra.Communication.Types.Protocol ()
import Pos.Binary.Class (decodeFull')
import Pos.Infra.Diffusion.Subscription.Status
(SubscriptionStatus (..))
import Pos.Infra.Util.LogSafe (BuildableSafeGen (..), buildSafe,
buildSafeList, buildSafeMaybe, deriveSafeBuildable,
plainOrSecureF)
plainOrSecureF, SecureLog)
import Test.Pos.Core.Arbitrary ()

-- | Declare generic schema, while documenting properties
@@ -1593,6 +1596,53 @@ instance BuildableSafeGen WalletSoftwareUpdate where
updBlockchainVersion
updScriptVersion

data WalletBalance = WalletBalance
{ wbBalance :: V1 (Core.Coin)
} deriving (Show, Eq, Generic)
deriveJSON Aeson.defaultOptions ''WalletBalance

instance ToSchema WalletBalance where
declareNamedSchema =
genericSchemaDroppingPrefix "wb" (\(--^) props -> props
& "balance"
--^ "the balance of a given public key"
)

instance Example WalletBalance
instance Arbitrary WalletBalance where
arbitrary = WalletBalance <$> arbitrary

instance Buildable WalletBalance where
build (WalletBalance bal) = "WalletBalance { ballence = " <> (show bal) <> " }"

instance ToSchema EncryptedSecretKey where
declareNamedSchema _ =
pure $ NamedSchema (Just "EncryptedSecretKey") $ mempty
& type_ .~ SwaggerString
& format ?~ "hex|base16"

instance ToJSON EncryptedSecretKey where
toJSON _esk = "todo"

mkEncryptedSecretKey :: Text -> Either Text EncryptedSecretKey
mkEncryptedSecretKey eskhex = join $
case Base16.decode eskhex of
Left e -> Left e
Right bs -> do
let
esk :: Either Text EncryptedSecretKey
esk = decodeFull' bs
pure esk

instance FromJSON EncryptedSecretKey where
parseJSON (String eskhex) = case mkEncryptedSecretKey eskhex of
Left e -> fail (toString e)
Right esk -> pure esk
parseJSON x = typeMismatch "parseJSON failed for EncryptedSecretKey" x

instance Buildable (SecureLog EncryptedSecretKey) where
build _ = "todo"

-- | A type encapsulating enough information to import a wallet from a
-- backup file.
data WalletImport = WalletImport
@@ -44,6 +44,7 @@ import Servant.Client (GenResponse (..), Response, ServantError (..))
import qualified Pos.Chain.Txp as Core
import Pos.Chain.Update (SoftwareVersion)
import qualified Pos.Core as Core
import Pos.Crypto (EncryptedSecretKey)

import Cardano.Wallet.API.Request.Filter
import Cardano.Wallet.API.Request.Pagination
@@ -155,6 +156,7 @@ data WalletClient m
:: m (Either ClientError ())
, importWallet
:: WalletImport -> Resp m Wallet
, queryBalance :: EncryptedSecretKey -> Resp m WalletBalance
} deriving Generic

data WalletDocClient m = WalletDocClient
@@ -286,6 +288,7 @@ natMapClient phi f wc = WalletClient
f $ phi $ resetWalletState wc
, importWallet =
f . phi . importWallet wc
, queryBalance = f . phi . queryBalance wc
}

-- | Run the given natural transformation over the 'WalletClient'.
@@ -108,6 +108,7 @@ mkHttpClient baseUrl manager = WalletClient

, importWallet
= run . importWalletR
, queryBalance = run . queryBalanceR
}

where
@@ -162,6 +163,7 @@ mkHttpClient baseUrl manager = WalletClient
:<|> postponeUpdateR
:<|> resetWalletStateR
:<|> importWalletR
:<|> queryBalanceR
= internalAPI

addressesAPI
@@ -51,7 +51,7 @@ import Cardano.Wallet.API.V1.Types (Account, AccountBalance,
NodeInfo, NodeSettings, PasswordUpdate, Payment,
Redemption, SpendingPassword, Transaction, V1 (..),
Wallet, WalletAddress, WalletId, WalletImport,
WalletUpdate)
WalletBalance, WalletUpdate)
import qualified Cardano.Wallet.Kernel.Accounts as Kernel
import qualified Cardano.Wallet.Kernel.Addresses as Kernel
import Cardano.Wallet.Kernel.CoinSelection.FromGeneric
@@ -482,6 +482,7 @@ data PassiveWalletLayer m = PassiveWalletLayer
, postponeUpdate :: m ()
, resetWalletState :: m ()
, importWallet :: WalletImport -> m (Either ImportWalletError Wallet)
, queryWalletBalance :: EncryptedSecretKey -> IO WalletBalance

-- updates
, waitForUpdate :: m ConfirmedProposalState
@@ -1,6 +1,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RankNTypes #-}

module Cardano.Wallet.WalletLayer.Kernel
( bracketPassiveWallet
@@ -12,6 +13,7 @@ import Universum hiding (for_)
import qualified Control.Concurrent.STM as STM
import Control.Monad.IO.Unlift (MonadUnliftIO)
import qualified Data.List.NonEmpty as NE
import qualified Data.Map as M (toList)

import Data.Foldable (for_)
import Formatting ((%))
@@ -20,16 +22,27 @@ import qualified Formatting as F
import Pos.Chain.Block (Blund, blockHeader, headerHash, prevBlockL)
import Pos.Chain.Genesis (Config (..))
import Pos.Core.Chrono (OldestFirst (..))
import Pos.Crypto (ProtocolMagic)
import Pos.Crypto (ProtocolMagic, EncryptedSecretKey)
import Pos.Infra.InjectFail (FInjects)
import Pos.Util.Wlog (Severity (Debug, Warning))

import Pos.Core.Common (Coin, unsafeAddCoin, mkCoin)
import Pos.Chain.Txp (TxOutAux, TxIn)
import Pos.Core.NetworkMagic (makeNetworkMagic)
import Pos.DB.Txp.Utxo (getAllPotentiallyHugeUtxo)
import Pos.Chain.Txp (Utxo, toaOut, txOutAddress, txOutValue)
import Pos.Chain.Update (HasUpdateConfiguration)
import Pos.Util.CompileInfo(HasCompileInfo)

import Cardano.Wallet.Kernel.Internal (walletProtocolMagic, walletNode)
import qualified Cardano.Wallet.Kernel as Kernel
import Cardano.Wallet.Kernel.PrefilterTx (WalletKey, filterOurs)
import Cardano.Wallet.Kernel.Decrypt (eskToWalletDecrCredentials)
import Cardano.Wallet.Kernel.Types (WalletId(WalletIdHdRnd))
import qualified Cardano.Wallet.Kernel.Actions as Actions
import qualified Cardano.Wallet.Kernel.BListener as Kernel
import Cardano.Wallet.Kernel.DB.AcidState (dbHdWallets)
import Cardano.Wallet.Kernel.DB.HdWallet (hdAccountRestorationState,
hdRootId, hdWalletsRoots)
hdRootId, hdWalletsRoots, eskToHdRootId, HdAddressId)
import qualified Cardano.Wallet.Kernel.DB.Read as Kernel
import qualified Cardano.Wallet.Kernel.DB.Util.IxSet as IxSet
import Cardano.Wallet.Kernel.Diffusion (WalletDiffusion (..))
@@ -47,6 +60,7 @@ import qualified Cardano.Wallet.WalletLayer.Kernel.Internal as Internal
import qualified Cardano.Wallet.WalletLayer.Kernel.Settings as Settings
import qualified Cardano.Wallet.WalletLayer.Kernel.Transactions as Transactions
import qualified Cardano.Wallet.WalletLayer.Kernel.Wallets as Wallets
import Cardano.Wallet.API.V1.Types (WalletBalance(..), V1(V1))

-- | Initialize the passive wallet.
-- The passive wallet cannot send new transactions.
@@ -144,6 +158,7 @@ bracketPassiveWallet pm mode logFunction keystore node fInjects f = do
, getTransactions = Transactions.getTransactions w
, getTxFromMeta = Transactions.toTransaction w
, getNodeSettings = Settings.getNodeSettings w
, queryWalletBalance = xqueryWalletBalance w
}
where
-- Read-only operations
@@ -153,6 +168,26 @@ bracketPassiveWallet pm mode logFunction keystore node fInjects f = do
invokeIO :: forall m'. MonadIO m' => Actions.WalletAction Blund -> m' ()
invokeIO = liftIO . STM.atomically . invoke

xqueryWalletBalance :: Kernel.PassiveWallet -> EncryptedSecretKey -> IO WalletBalance
xqueryWalletBalance w esk = do
let
nm = makeNetworkMagic (w ^. walletProtocolMagic)
wId = WalletIdHdRnd (eskToHdRootId nm esk)
wKey :: WalletKey
wKey = (wId, eskToWalletDecrCredentials nm esk)
withNode :: (HasCompileInfo, HasUpdateConfiguration) => Lock (WithNodeState IO) -> WithNodeState IO Utxo
withNode _lock = getAllPotentiallyHugeUtxo
--print wKey
all_utxo <- withNodeState (w ^. walletNode) withNode
--mapM_ print all_utxo
let
my_utxo = filterOurs wKey (txOutAddress . toaOut . snd) (M.toList all_utxo)
sumUtxo :: Coin -> ((TxIn, TxOutAux), HdAddressId) -> Coin
sumUtxo a b = unsafeAddCoin a ((txOutValue . toaOut . snd . fst) b)
balance :: Coin
balance = foldl' sumUtxo (mkCoin 0) my_utxo
pure $ WalletBalance $ V1 balance

-- | Initialize the active wallet.
-- The active wallet is allowed to send transactions, as it has the full
-- 'WalletDiffusion' layer in scope.

0 comments on commit d7ff59c

Please sign in to comment.
You can’t perform that action at this time.