Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--sync-tolerance to control the tolerance in the sync progress calculation #991

Merged
merged 4 commits into from
Nov 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/cli/src/Cardano/CLI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ module Cardano.CLI
, stateDirOption
, loggingConfigFileOption
, verbosityOption
, syncToleranceOption

-- * Types
, Service
Expand Down Expand Up @@ -131,7 +132,14 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
import Cardano.Wallet.Primitive.Mnemonic
( entropyToMnemonic, genEntropy, mnemonicToText )
import Cardano.Wallet.Primitive.Types
( AddressState, Hash, PoolId, SortOrder, WalletId, WalletName )
( AddressState
, Hash
, PoolId
, SortOrder
, SyncTolerance (..)
, WalletId
, WalletName
)
import Cardano.Wallet.Version
( showVersion, version )
import Control.Applicative
Expand Down Expand Up @@ -876,6 +884,20 @@ stateDirOption backendDir = optional $ strOption $ mempty
where
defaultDir = backendDir </> "NETWORK"

-- | --sync-tolerance=DURATION, default: 300s
syncToleranceOption :: Parser SyncTolerance
syncToleranceOption = optionT $ mempty
<> long "sync-tolerance"
<> metavar "DURATION"
<> help (mconcat
[ "time duration within which we consider being synced with the "
, "network. Expressed in seconds with a trailing 's'."
])
<> value fiveMinutes
<> showDefaultWith showT
where
fiveMinutes = SyncTolerance (5*60)

-- | [--start=TIME]
timeRangeStartOption :: Parser Iso8601Time
timeRangeStartOption = optionT $ mempty
Expand Down
1 change: 1 addition & 0 deletions lib/core/cardano-wallet-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ library
, persistent-template
, random
, retry
, safe
, servant
, servant-server
, split
Expand Down
30 changes: 21 additions & 9 deletions lib/core/src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ import Cardano.Wallet.Primitive.Types
, SlotParameters (..)
, SortOrder (..)
, SyncProgress (..)
, SyncTolerance
, TransactionInfo (..)
, Tx
, TxIn (..)
Expand Down Expand Up @@ -315,7 +316,7 @@ import qualified Data.Set as Set
data WalletLayer s t (k :: Depth -> * -> *)
= WalletLayer
(Trace IO Text)
(Block, BlockchainParameters)
(Block, BlockchainParameters, SyncTolerance)
(NetworkLayer IO t Block)
(TransactionLayer t k)
(DBLayer IO s k)
Expand Down Expand Up @@ -353,7 +354,7 @@ data WalletLayer s t (k :: Depth -> * -> *)
-- and their metadata does not require any networking layer.
type HasDBLayer s k = HasType (DBLayer IO s k)

type HasGenesisData = HasType (Block, BlockchainParameters)
type HasGenesisData = HasType (Block, BlockchainParameters, SyncTolerance)

type HasLogger = HasType (Trace IO Text)

Expand All @@ -371,9 +372,9 @@ dbLayer =

genesisData
:: forall ctx. HasGenesisData ctx
=> Lens' ctx (Block, BlockchainParameters)
=> Lens' ctx (Block, BlockchainParameters, SyncTolerance)
genesisData =
typed @(Block, BlockchainParameters)
typed @(Block, BlockchainParameters, SyncTolerance)

logger
:: forall ctx. HasLogger ctx
Expand Down Expand Up @@ -424,7 +425,7 @@ createWallet ctx wid wname s = db & \DBLayer{..} -> do
initializeWallet (PrimaryKey wid) cp meta hist $> wid
where
db = ctx ^. dbLayer @s @k
(block0, bp) = ctx ^. genesisData
(block0, bp, _) = ctx ^. genesisData

-- | Retrieve the wallet state for the wallet with the given ID.
readWallet
Expand All @@ -441,11 +442,19 @@ readWallet ctx wid = db & \DBLayer{..} -> mapExceptT atomically $ do
where
db = ctx ^. dbLayer @s @k

walletSyncProgress :: Wallet s -> IO SyncProgress
walletSyncProgress w = do
walletSyncProgress
:: forall ctx s.
( HasGenesisData ctx
)
=> ctx
-> Wallet s
-> IO SyncProgress
walletSyncProgress ctx w = do
let bp = blockchainParameters w
let h = currentTip w
syncProgressRelativeToTime (slotParams bp) h <$> getCurrentTime
syncProgressRelativeToTime st (slotParams bp) h <$> getCurrentTime
where
(_, _, st) = ctx ^. genesisData

-- | Update a wallet's metadata with the given update function.
updateWallet
Expand Down Expand Up @@ -500,6 +509,7 @@ restoreWallet
( HasLogger ctx
, HasNetworkLayer t ctx
, HasDBLayer s k ctx
, HasGenesisData ctx
)
=> ctx
-> WalletId
Expand All @@ -523,6 +533,7 @@ restoreBlocks
:: forall ctx s k.
( HasLogger ctx
, HasDBLayer s k ctx
, HasGenesisData ctx
)
=> ctx
-> WalletId
Expand Down Expand Up @@ -553,7 +564,7 @@ restoreBlocks ctx wid blocks nodeTip = db & \DBLayer{..} -> do
putTxHistory (PrimaryKey wid) txs

liftIO $ do
progress <- walletSyncProgress (NE.last cps)
progress <- walletSyncProgress @ctx ctx (NE.last cps)
logInfo tr $
pretty meta
logInfo tr $ "syncProgress: " <> pretty progress
Expand All @@ -568,6 +579,7 @@ restoreBlocks ctx wid blocks nodeTip = db & \DBLayer{..} -> do
where
db = ctx ^. dbLayer @s @k
tr = ctx ^. logger

logCheckpoint :: Wallet s -> IO ()
logCheckpoint cp = logInfo tr $
"Creating checkpoint at " <> pretty (currentTip cp)
Expand Down
10 changes: 8 additions & 2 deletions lib/core/src/Cardano/Wallet/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,13 @@ import Cardano.Wallet.Primitive.AddressDerivation
import Cardano.Wallet.Primitive.Model
( BlockchainParameters )
import Cardano.Wallet.Primitive.Types
( AddressState, Block, PoolId, SortOrder (..), WalletId (..) )
( AddressState
, Block
, PoolId
, SortOrder (..)
, SyncTolerance
, WalletId (..)
)
import Cardano.Wallet.Registry
( HasWorkerCtx (..), WorkerRegistry )
import Cardano.Wallet.Transaction
Expand Down Expand Up @@ -375,7 +381,7 @@ instance Accept Any where
data ApiLayer s t (k :: Depth -> * -> *)
= ApiLayer
(Trace IO Text)
(Block, BlockchainParameters)
(Block, BlockchainParameters, SyncTolerance)
(NetworkLayer IO t (Block))
(TransactionLayer t k)
(DBFactory IO s k)
Expand Down
9 changes: 5 additions & 4 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ import Cardano.Wallet.Primitive.Types
, PoolId
, SortOrder (..)
, SyncProgress (..)
, SyncTolerance
, TransactionInfo (TransactionInfo)
, Tx (..)
, TxIn
Expand Down Expand Up @@ -817,7 +818,7 @@ network ctx = do
let ntrkTip = fromMaybe slotMinBound (slotAt sp now)
pure $ ApiNetworkInformation
{ syncProgress =
ApiT $ syncProgressRelativeToTime sp nodeTip now
ApiT $ syncProgressRelativeToTime st sp nodeTip now
, nodeTip =
ApiBlockReference
{ epochNumber = ApiT $ nodeTip ^. (#slotId . #epochNumber)
Expand All @@ -832,7 +833,7 @@ network ctx = do
}
where
nl = ctx ^. networkLayer @t
(_, bp) = ctx ^. genesisData
(_, bp, st) = ctx ^. genesisData
sp :: W.SlotParameters
sp = W.SlotParameters
(bp ^. #getEpochLength)
Expand Down Expand Up @@ -1127,7 +1128,7 @@ getWalletWithCreationTime mk ctx (ApiT wid) = do
(wallet, meta, pending) <-
liftHandler $ withWorkerCtx ctx wid throwE $
\wrk -> W.readWallet wrk wid
progress <- liftIO $ W.walletSyncProgress wallet
progress <- liftIO $ W.walletSyncProgress ctx wallet
return (mk wid wallet meta progress pending, meta ^. #creationTime)

mkApiWallet
Expand Down Expand Up @@ -1188,7 +1189,7 @@ getWalletTip wallet = ApiBlockReference
newApiLayer
:: forall ctx s t k. ctx ~ ApiLayer s t k
=> Trace IO Text
-> (Block, BlockchainParameters)
-> (Block, BlockchainParameters, SyncTolerance)
-> NetworkLayer IO t Block
-> TransactionLayer t k
-> DBFactory IO s k
Expand Down
56 changes: 49 additions & 7 deletions lib/core/src/Cardano/Wallet/Primitive/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ module Cardano.Wallet.Primitive.Types

-- * Slotting
, SyncProgress(..)
, SyncTolerance(..)
, mkSyncTolerance
, SlotId (..)
, SlotNo (..)
, EpochNo (..)
Expand Down Expand Up @@ -213,6 +215,8 @@ import GHC.TypeLits
( KnownSymbol, Symbol, symbolVal )
import Numeric.Natural
( Natural )
import Safe
( readMay )

import qualified Control.Foldl as F
import qualified Data.ByteString as BS
Expand Down Expand Up @@ -1023,6 +1027,34 @@ instance Buildable SyncProgress where
Syncing (Quantity p) ->
"still restoring (" <> build (toText p) <> ")"

newtype SyncTolerance = SyncTolerance NominalDiffTime
deriving stock (Generic, Eq, Show)

-- | Construct a 'SyncTolerance' from a number of __seconds__
mkSyncTolerance :: Int -> SyncTolerance
mkSyncTolerance =
SyncTolerance . toEnum . (* pico)
where
pico = 1000*1000*1000*1000

instance ToText SyncTolerance where
toText (SyncTolerance t) = T.pack (show t)

instance FromText SyncTolerance where
fromText t = case T.splitOn "s" t of
[v,""] ->
maybe
(Left errSyncTolerance)
(Right . mkSyncTolerance)
(readMay $ T.unpack v)
_ ->
Left errSyncTolerance
where
errSyncTolerance = TextDecodingError $ unwords
[ "Cannot parse given time duration. Here are a few examples of"
, "valid text representing a sync tolerance: '3s', '3600s', '42s'."
]

-- | Estimate restoration progress based on:
--
-- - The current local tip
Expand All @@ -1047,19 +1079,26 @@ instance Buildable SyncProgress where
-- `h` becomes bigger and `X` becomes smaller making the progress estimation
-- better and better. At some point, `X` is null, and we have `p = h / h`
syncProgress
:: EpochLength
-- ^ Known epoch length
:: SyncTolerance
-- ^ A time tolerance inside which we consider ourselves synced
-> SlotParameters
-- ^ Parameters relative to slot arithmetics
-> BlockHeader
-- ^ Local tip
-> SlotId
-- ^ Last slot that could have been produced
-> SyncProgress
syncProgress epochLength tip slotNow =
syncProgress (SyncTolerance timeTolerance) sp tip slotNow =
let
bhTip = fromIntegral . getQuantity $ tip ^. #blockHeight

epochLength = sp ^. #getEpochLength
(SlotLength slotLength) = (sp ^. #getSlotLength)

n0 = flatSlot epochLength (tip ^. #slotId)
n1 = flatSlot epochLength slotNow
tolerance = 5

tolerance = floor (timeTolerance / slotLength)
in if distance n1 n0 < tolerance || n0 >= n1 then
Ready
else
Expand All @@ -1069,16 +1108,19 @@ syncProgress epochLength tip slotNow =
-- | Helper to compare the /local tip/ with the slot corresponding to a
-- @UTCTime@, and calculate progress based on that.
syncProgressRelativeToTime
:: SlotParameters
:: SyncTolerance
-- ^ A time tolerance inside which we consider ourselves synced
-> SlotParameters
-- ^ Parameters relative to slot arithmetics
-> BlockHeader
-- ^ Local tip
-> UTCTime
-- ^ Where we believe the network tip is (e.g. @getCurrentTime@).
-> SyncProgress
syncProgressRelativeToTime sp tip time =
syncProgressRelativeToTime tolerance sp tip time =
maybe
(Syncing minBound)
(syncProgress (sp ^. #getEpochLength) tip)
(syncProgress tolerance sp tip)
(slotAt sp time)

-- | Convert a 'SlotId' to the number of slots since genesis.
Expand Down
Loading