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

Reorganize modules in Cardano.Wallet.DB.* #3046

Merged
merged 1 commit into from
Dec 10, 2021

Conversation

HeinrichApfelmus
Copy link
Contributor

Issue number

ADP-1288

Overview

Previous work in epic ADP-1043 introduced delta encodings, DBVars, and an embedding of the wallet state and its delta encodings into a database table. It's time to integrate these tools with the wallet code. To facilitate code review, the integration proceeds in a sequence of refactorings that do not change functionality and pass all unit tests.

In this step, we reorganize the modules in the Cardano.Wallet.DB.* hierarchy; the aim is to be able to quickly swap out the implementation of the mkStoreWalletsCheckpoints function by changing one import. We want to keep both the previous implementation and a subsequent new implementation around for some time.

Details

  • The migration function migrateManually is moved to a separate module Cardano.Wallet.DB.Sqlite.Migration. This has the beneficial side effect that this migration function cannot be accidentally be affected by refactoring in modules that are not imported by this module.
  • The creation of the checkpoints store, mkStoreWalletsCheckpoints, is moved to a separate module. We can swap out its implementation by importing a different module in the future.

Comments

Merge PR #2942 before this one, because this pull request is based on the branch of the former.


-- | Get the checkpoint with the largest 'SlotNo'.
getLatest :: Checkpoints a -> (W.SlotPoint, a)
getLatest = from . Map.lookupMax . view #checkpoints
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not to use Either here with explicit Error. use could then also use maybeToLeft https://hackage.haskell.org/package/either-5.0.1.1/docs/src/Data.Either.Combinators.html#maybeToLeft

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. This error case would be a programming error, as we assume that a checkpoint for genesis is always stored, so I thought it would be fine to use a partial function here. 🤔

But I can see that if we consider Checkpoints as an abstraction independent of the database, then the error might in principle occur, e.g. we have

getLatest (apply (RollbackTo pt2) (singleton pt1 a)) = _|_

I think that the appropriate solution is actually to change singleton to only accept the genesis slot (and rename the function to fromGenesis) and to comment that RestricTo will never remove the genesis checkpoint. Then, the Checkpoints data type will satisfy the invariant that the map is always nonempty.

I will add a change to this effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm beginning to confuse myself by doing a lot rebasing, so I would like to postpone the introduction of fromGenesis to the next PR in this series.


-- | Magic value denoting the hash of a block that \"does not exist\".
-- Specifically, this is the hash of \"the parent block\" of the genesis block.
hashOfNoParent :: Hash "BlockHeader"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another approach could be to use introduce something like "parent" with Maybe...and in that case no need for magic values...it would be just Nothing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. 🤔 I agree — but unfortunately, the database does serialize the parentHeaderHash in the table:

https://github.com/input-output-hk/cardano-wallet/blob/3a24c14b3208da7a6e890de1c2f3f261ab8d5b01/lib/core/src/Cardano/Wallet/DB/Sqlite/TH.hs#L210

and then we would have the magic value in the serialization. But I suppose that is an improvement over having the magic value in the type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we could probably deal with this magic value at the level of PersistField instance or so. and everywhere above we could have Maybe. just suggestion

Copy link
Contributor

@paweljakubas paweljakubas Dec 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so have magic value defined somewhere here https://github.com/input-output-hk/cardano-wallet/blob/master/lib/core/src/Cardano/Wallet/DB/Sqlite/Types.hs and toPersistValue Nothing = hashOfNoParent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the hashOfNoParent constant to this module, but stopped short of changing the BlockId type. Instead, I have created two conversion functions.

Copy link
Contributor

@paweljakubas paweljakubas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like reorganizations adopted. It is seen what is new, what is old, and also that we don't affect with Checkpoints migration code at all now.

I like Checkpoint step 1 refactoring. It makes things easier to understand. To my best knowledge logic of checkpoint lifecycle is maintained (which is further evidenced by all current tests passing). I would think about introducing Maybe in parentBlockHeader and hence have Nothing rather than magic value for first block header.

Good job! Waiting for another iteration!

Also would be good if other team member would review this PR (for both double check and educational/knowledge sharing purposes)

@HeinrichApfelmus HeinrichApfelmus force-pushed the HeinrichApfelmus/ADP-1043/1288-reorganize-modules branch 6 times, most recently from a2ac138 to 417ac12 Compare December 6, 2021 15:28
Move

* manual database migrations
* `Checkpoints` type
* and the creation of a `Store` for checkpoints

into separate modules. The intention is that the store creation will be swapped out by a different implementation later on.
@HeinrichApfelmus HeinrichApfelmus force-pushed the HeinrichApfelmus/ADP-1043/1288-reorganize-modules branch from 417ac12 to 2a222c5 Compare December 6, 2021 16:59
@HeinrichApfelmus HeinrichApfelmus changed the base branch from master to HeinrichApfelmus/ADP-1043/integration-1169 December 7, 2021 12:11
Comment on lines +48 to +49
Historical hack. The DB layer can't represent 'Origin' in the database,
instead we have mapped it to 'SlotNo 0', which is wrong.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't matter - as you write below, we don't need rollback to genesis. Only the unstable blocks can be rolled back.

>>> [cardano-wallet.pools-engine:Error:1293] [2020-11-24 10:02:04.00 UTC]
>>> Couldn't store production for given block before it conflicts with
>>> another block. Conflicting block header is:
>>> 5bde7e7b<-[f1b35b98-4290#2008]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this looks more like rollback isn't quite working correctly in the pools db.
The #2008 means block height 2008, which is a long way from genesis.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. Good point.

Copy link
Contributor Author

@HeinrichApfelmus HeinrichApfelmus Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did receive reports were #0 is affected:

Unexpected error following the chain:user error (restoreBlocks: given chain isn't a valid continuation. Wallet is at: 891fcd4b<-[9c9290a4-0#0] but the given chain continues starting from: 891fcd4b<-[db619f76-14#0])

Full log: https://gist.github.com/runeksvendsen/c143926249e32b22ee2d184581c9776c

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. But the pool db problem could be unrelated then.

Base automatically changed from HeinrichApfelmus/ADP-1043/integration-1169 to master December 8, 2021 12:57
@HeinrichApfelmus
Copy link
Contributor Author

bors merge

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Dec 10, 2021

Build succeeded:

@iohk-bors iohk-bors bot merged commit 0ccc9e8 into master Dec 10, 2021
@iohk-bors iohk-bors bot deleted the HeinrichApfelmus/ADP-1043/1288-reorganize-modules branch December 10, 2021 10:14
WilliamKingNoel-Bot pushed a commit that referenced this pull request Dec 10, 2021
iohk-bors bot added a commit that referenced this pull request Dec 16, 2021
3048: Restrict `Checkpoints` to be constructible from genesis only r=HeinrichApfelmus a=HeinrichApfelmus

### Issue number

ADP-1043

### Overview

Previous work in epic ADP-1043 introduced delta encodings, DBVars, and an embedding of the wallet state and its delta encodings into a database table. It's time to integrate these tools with the wallet code. To facilitate code review, the integration proceeds in a sequence of refactorings that do not change functionality and pass all unit tests.

In this step, we implement a suggestion that came up during review where we remove the `singleton` function in favor of `fromGenesis`, making sure that the `Checkpoints` structure always contains a checkpoint for the genesis block.

### Details

* The `initializeWallet` function will now throw an exception if it is incorrectly called with a checkpoint that is not at the genesis block.
* Many unit tests are adapted to use the `InitialCheckpoint` generator in order to comply with the above restriction.

### Comments

Merge PR #3046 before this one, because this pull request is based on the branch of the former.


Co-authored-by: Heinrich Apfelmus <[email protected]>
iohk-bors bot added a commit that referenced this pull request Dec 16, 2021
3048: Restrict `Checkpoints` to be constructible from genesis only r=HeinrichApfelmus a=HeinrichApfelmus

### Issue number

ADP-1043

### Overview

Previous work in epic ADP-1043 introduced delta encodings, DBVars, and an embedding of the wallet state and its delta encodings into a database table. It's time to integrate these tools with the wallet code. To facilitate code review, the integration proceeds in a sequence of refactorings that do not change functionality and pass all unit tests.

In this step, we implement a suggestion that came up during review where we remove the `singleton` function in favor of `fromGenesis`, making sure that the `Checkpoints` structure always contains a checkpoint for the genesis block.

### Details

* The `initializeWallet` function will now throw an exception if it is incorrectly called with a checkpoint that is not at the genesis block.
* Many unit tests are adapted to use the `InitialCheckpoint` generator in order to comply with the above restriction.

### Comments

Merge PR #3046 before this one, because this pull request is based on the branch of the former.


Co-authored-by: Heinrich Apfelmus <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants