-
Notifications
You must be signed in to change notification settings - Fork 217
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
Allow redelegation to the same pool in Conway #4562
Allow redelegation to the same pool in Conway #4562
Conversation
bc651c6
to
8c31931
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think that this file should not be modified. 😅 The reason is that allowing or disallowing delegation is the main concern of the function guardJoin
— any changes should be localized to that function or its immediate context. (The next larger context for guardJoin
is the function joinStakePoolDelegationAction
).
That said, we have split the stake pool delegation and the voting parts a little bit — and they are joined only here, which is not a good separation of concerns. Ideally, the logic would be joined at voteAction
and handleDelegationRequest
. I see two options to choose from:
- Make a function
handleVoteAndDelegateRequest
in the moduleCardano.Wallet.IO.Delegation
which merges thehandleDelegationRequest
andvoteAction
, and returnsIO (Tx.DelegationAction, Tx.VotingAction)
. This function handles the interaction between the two concepts. This moves the concern out of theServer
module where it doesn't belong. - Go back to the previous version where we unconditionally allow joining a separate stake pool. This would simplify the implementation tremendously (at the expense of having a future change do 1 anyway.)
I leave the choice up to you. 🤓
(PS: Once the concern has been moved to handleVoteAndDelegateRequest
, I will ask you to split off a pure function again, along the lines of how joinStakePoolDelegationAction
has been split into a pure variant and an IO
variant.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added handleVoteAndDelegateRequest
👍
}] | ||
}|] | ||
rTx2 <- request @(ApiConstructTransaction n) ctx | ||
(Link.createUnsignedTransaction @'Shelley src) Default delegationJoin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Link.createUnsignedTransaction @'Shelley src) Default delegationJoin |
Alas, Daedalus still uses the old joinStakePool
endpoint
https://cardano-foundation.github.io/cardano-wallet/api/edge/#operation/joinStakePool
Can you write the additional integration tests to be only about this endpoint?
I don't think it makes sense to test Link.createUnsignedTransaction
at this level — it's a kitchen-sink function, other tests are checking that it's behavior hasn't changed too much; I think that is sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added joinStakePool tests showing the case for both Babbage and Conway era. I would argue testing in new tx workflow is also crucial as we want to be consistent here and outlaw some action (those that are redundant and incur fees)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue testing in new tx workflow is also crucial as we want to be consistent here and outlaw some action (those that are redundant and incur fees)
Well in this case, I would argue that a user of createUnsignedTransaction
is a programmer and knows what they are doing, whereas joinStakePool
is a proxy for actions from Daedalus users. So the latter would benefit from not incurring unnecessary fees. But I leave it up to you.
@@ -207,21 +263,21 @@ prop_guardJoinQuit knownPoolsList dlg pid wdrl mRetirementInfo = checkCoverage | |||
pure $ W.currentEpoch info >= W.retirementEpoch info | |||
|
|||
prop_guardQuitJoin | |||
:: NonEmptyList PoolId | |||
:: (Set PoolId -> WalletDelegation -> PoolId -> Maybe PoolRetirementEpochInfo -> Maybe Bool -> Either ErrCannotJoin ()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:: (Set PoolId -> WalletDelegation -> PoolId -> Maybe PoolRetirementEpochInfo -> Maybe Bool -> Either ErrCannotJoin ()) | |
:: GuardQuitJoinFun |
A type synonym
type GuardQuitJoinFun = Set PoolId -> WalletDelegation -> PoolId -> Maybe PoolRetirementEpochInfo -> Maybe Bool -> Either ErrCannotJoin ()
helps with understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -117,26 +118,36 @@ joinStakePoolDelegationAction | |||
W.getPoolRetirementCertificate poolStatus | |||
|
|||
guardJoin | |||
:: Set PoolId | |||
:: Write.IsRecentEra era | |||
=> Write.RecentEra era |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
6901504
to
c61c1e8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! 😊 I still have a couple of comments
Importantly, I think that there is still a corner case in joinStakePoolAction
where a user may be prevented from delegating to a stake pool if they are voting but not delegating to a pool (a state of affairs which has to be brought about by a different wallet). 🤔 I believe that changing the Maybe Bool
type to an enumeration type with more speaking constructors (or additional constructors) may help resolve this issue.
pure (Nothing, Nothing) | ||
optionalDelegationAction <- forM delRequestM $ | ||
handleDelegationRequest ctx currentEpochSlotting getKnownPools | ||
getPoolStatus withdrawal votingSameAgain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getPoolStatus withdrawal votingSameAgain | |
getPoolStatus withdrawal votingSameAgain |
Indentation for clarity.
(_,(_, dlg),_) <- W.readWallet ctx | ||
(_, stakeKeyIsRegistered) <- db & \DBLayer{atomically,walletState} -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(_,(_, dlg),_) <- W.readWallet ctx | |
(_, stakeKeyIsRegistered) <- db & \DBLayer{atomically,walletState} -> | |
(calculateWalletDelegations, stakeKeyIsRegistered) <- db & \DBLayer{atomically,walletState} -> |
Could you remove the call to W.readWallet
here, and instead inline the computation of dlg
from the result of readDelegation
, like this:
currentEpochSlotting <- getCurrentEpochSlotting nl
let dlg = calculateWalletDelegations currentEpochSlotting
This improves the separation of concerns — modules that are higher in the hierarchy (Cardano.Wallet
) should be assembled from children that are lower in the hierarchy (Cardano.Wallet.*
), not the other way round.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, thanks
(_,(_, dlg),_) <- W.readWallet ctx | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(_,(_, dlg),_) <- W.readWallet ctx |
As in the other comment — for better separation of concerns, could you compute dlg
by inlining the call to W.readWallet
here? Compared to the situation above, the value currentEpochSlotting
is now already in scope.
specifications/api/swagger.yaml
Outdated
properties: | ||
message: | ||
type: string | ||
description: May occur in Conway onwards when the same vote was casted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description: May occur in Conway onwards when the same vote was casted. | |
description: May occur in Conway onwards when the same vote was cast. |
The past participle is "cast".
specifications/api/swagger.yaml
Outdated
properties: | ||
message: | ||
type: string | ||
description: May occur in Conway onwards when a given poolId matches the current delegation preferences of the wallet's account and also the same vote was casted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description: May occur in Conway onwards when a given poolId matches the current delegation preferences of the wallet's account and also the same vote was casted. | |
description: May occur in Conway onwards when a given poolId matches the current delegation preferences of the wallet's account and also the same vote was cast. |
Nothing -> | ||
pure Nothing | ||
(optionalDelegationAction, optionalVoteAction) <- liftIO $ | ||
IODeleg.handleDelegationVoteRequest wrk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
-> WalletDelegation | ||
-> PoolId | ||
-> Maybe PoolRetirementEpochInfo | ||
-> Maybe Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> Maybe Bool | |
-> VoteChange |
I find the meaning of this Maybe Bool
argument difficult to understand ("boolean blindness"). Given the correctness issue in joinStakePool
, I would like to have an type with more speaking names here. How about
data VoteChange = Unimportant | NotVotingToVoting | VotingToAny
Hm, actually, I don't now what to put here. 🤔
I think that joinStakePoolDelegationAction
uses the following logic:
- If the wallet is not delegating, it's ok.
- If the wallet is delegating, but not voting, it's ok.
- If the wallet is delegating, and already voting — to abstain or to any other DRep —, then
guardJoin
should disallow the action.
However, for handleDelegationVoteRequest
, the logic is different
- If the wallet is delegating, and already voting — to abstain or to any other DRep —, then it's ok unless the new vote is exactly the same as the old vote.
If you ask me, the easiest solution is probably to let handleDelegationVoteRequest
allow everything, so that constructUnsignedTransaction
will always succeed. 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
data VoteRequest = NotVotedYet | VotedSameLikeBefore | VotedDifferently
to address logic refactoring
}] | ||
}|] | ||
rTx2 <- request @(ApiConstructTransaction n) ctx | ||
(Link.createUnsignedTransaction @'Shelley src) Default delegationJoin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue testing in new tx workflow is also crucial as we want to be consistent here and outlaw some action (those that are redundant and incur fees)
Well in this case, I would argue that a user of createUnsignedTransaction
is a programmer and knows what they are doing, whereas joinStakePool
is a proxy for actions from Daedalus users. So the latter would benefit from not incurring unnecessary fees. But I leave it up to you.
waitForTxStatus ctx w InLedger . getResponse | ||
=<< joinStakePool @n ctx (SpecificPool pool) (w, fixturePassphrase) | ||
|
||
eventually "Wallet is delegating to pool and voting abstain" $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
r <- joinStakePool @n ctx (SpecificPool pool) (w, fixturePassphrase) | ||
verify r | ||
[ expectResponseCode HTTP.status403 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(stray newline)
c61c1e8
to
0a3c191
Compare
41c157e
to
c4f9079
Compare
code format code format
c4f9079
to
bd71f61
Compare
fix DelegationSpec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good now, thank you! 😊 One last request on grammar, though.
@@ -75,6 +82,9 @@ data DelegationRequest | |||
-- ^ Stop delegating if the wallet is delegating. | |||
deriving (Eq, Show) | |||
|
|||
data VoteRequest = NotVotedYet | VotedSameLikeBefore | VotedDifferently |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data VoteRequest = NotVotedYet | VotedSameLikeBefore | VotedDifferently | |
data VoteRequest = NotVotedYet | VotedSameAsBefore | VotedDifferently |
This is grammatically better. 😅
…il in a few bullet points the work accomplished in this PR. Before you submit, don't forget to: CODE-OF-CONDUCT.md CONTRIBUTING.md LICENSE MAINTAINERS.md README.md cabal.project configs default.nix docker-compose.yml docs flake.lock flake.nix floskell.json fourmolu.yaml hie-direnv.yaml hie.yaml justfile lib nix prototypes reports scripts shell.nix specifications test touch.me.CI weeder.dhall Make sure the GitHub PR fields are correct: ✓ Set a good Title for your PR. ✓ Assign yourself to the PR. ✓ Assign one or more reviewer(s). ✓ Link to a Jira issue, and/or other GitHub issues or PRs. ✓ In the PR description delete any empty sections and all text commented in <!--, so that this text does not appear in merge commit messages. CODE-OF-CONDUCT.md CONTRIBUTING.md LICENSE MAINTAINERS.md README.md cabal.project configs default.nix docker-compose.yml docs flake.lock flake.nix floskell.json fourmolu.yaml hie-direnv.yaml hie.yaml justfile lib nix prototypes reports scripts shell.nix specifications test touch.me.CI weeder.dhall Don't waste reviewers' time: ✓ If it's a draft, select the Create Draft PR option. ✓ Self-review your changes to make sure nothing unexpected slipped through. CODE-OF-CONDUCT.md CONTRIBUTING.md LICENSE MAINTAINERS.md README.md cabal.project configs default.nix docker-compose.yml docs flake.lock flake.nix floskell.json fourmolu.yaml hie-direnv.yaml hie.yaml justfile lib nix prototypes reports scripts shell.nix specifications test touch.me.CI weeder.dhall Try to make your intent clear: ✓ Write a good Description that explains what this PR is meant to do. ✓ Jira will detect and link to this PR once created, but you can also link this PR in the description of the corresponding Jira ticket. ✓ Highlight what Testing you have done. ✓ Acknowledge any changes required to the Documentation. --> - [x] Change of re-delegation logic. - [x] Adjust unit tests - [x] Add new unit tests - [x] Add integration testing showing the case - [x] Babbage: re-delegation to the same pool -> ERROR - [x] Conway: re-delegation to the same pool -> ERROR - [x] Conway: re-delegation to the same pool + new vote or re-vote other than recent -> OK - [x] Conway: re-delegation to the same pool + vote the same again -> ERROR - [x] Conway: re-delegation to the different pool + vote the same again -> OK - [x] Conway: vote the same again -> ERROR - [x] check joinStakePools - [x] add handleDelegationVoteRequest ### Comments In order to run integration tests do the following: (a) Babbage ``` just integration-tests-cabal-match TRANS_NEW_JOIN_01f ``` (b) Conway ``` just conway-integration-tests-cabal-match TRANS_NEW_JOIN_01f ``` <!-- Additional comments, links, or screenshots to attach, if any. --> ### Issue Number adp 3312 <!-- Reference the Jira/GitHub issue that this PR relates to, and which requirements it tackles. Note: Jira issues of the form ADP- will be auto-linked. --> Source commit: cb43316
Comments
In order to run integration tests do the following:
(a) Babbage
(b) Conway
Issue Number
adp 3312