Skip to content

Commit

Permalink
Add certs to the response construct tx endpoint (#4892)
Browse files Browse the repository at this point in the history
<!--
Detail in a few bullet points the work accomplished in this PR.

Before you submit, don't forget to:

* 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.

* 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.

* 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] Adding voting certs to the response of constructTransaction (until
now only delegation certs were supported)
- [x] test all cases in the existent integration tests

### Comments

<!-- Additional comments, links, or screenshots to attach, if any. -->

### Issue Number
fix #4871

<!-- 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. -->
  • Loading branch information
abailly authored Dec 30, 2024
2 parents 630ef00 + 45ea3b7 commit 42b154c
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 53 deletions.
46 changes: 35 additions & 11 deletions lib/api/src/Cardano/Wallet/Api/Http/Shelley/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1978,7 +1978,7 @@ selectCoins ctx@ApiLayer {..} argGenChange (ApiT walletId) body = do
, outputs = mkApiCoinSelectionOutput <$> outputs
, change = mkApiCoinSelectionChange <$> change
, collateral = mkApiCoinSelectionCollateral <$> collateral
, certificates = uncurry mkApiCoinSelectionCerts <$>
, certificates = uncurry mkApiCoinSelectionDelCerts <$>
delegationAction
, withdrawals = mkApiCoinSelectionWithdrawal <$> withdrawals
, depositsTaken = maybeToList $ ApiAmount.fromCoin <$> deposit
Expand Down Expand Up @@ -2018,7 +2018,7 @@ selectCoinsForJoin ctx knownPools getPoolStatus poolId walletId = do
, outputs = mkApiCoinSelectionOutput <$> outputs
, change = mkApiCoinSelectionChange <$> change
, collateral = mkApiCoinSelectionCollateral <$> collateral
, certificates = uncurry mkApiCoinSelectionCerts <$>
, certificates = uncurry mkApiCoinSelectionDelCerts <$>
delegationAction
, withdrawals = mkApiCoinSelectionWithdrawal <$> withdrawals
, depositsTaken = maybeToList $ ApiAmount.fromCoin <$> deposit
Expand Down Expand Up @@ -2047,7 +2047,7 @@ selectCoinsForQuit ctx (ApiT walletId) = do
, outputs = mkApiCoinSelectionOutput <$> outputs
, change = mkApiCoinSelectionChange <$> change
, collateral = mkApiCoinSelectionCollateral <$> collateral
, certificates = uncurry mkApiCoinSelectionCerts <$>
, certificates = uncurry mkApiCoinSelectionDelCerts <$>
delegationAction
, withdrawals = mkApiCoinSelectionWithdrawal <$> withdrawals
, depositsTaken = maybeToList $ ApiAmount.fromCoin <$> deposit
Expand Down Expand Up @@ -2801,6 +2801,7 @@ constructTransaction api knownPools poolStatus apiWalletId body = do
deposits
refunds
((,rewardPath) <$> transactionCtx3 ^. #txDelegationAction)
((,rewardPath) <$> transactionCtx3 ^. #txVotingAction)
metadata
(unsignedTx rewardPath (outs ++ mintingOuts) apiDecoded)
, fee = apiDecoded ^. #fee
Expand Down Expand Up @@ -3240,7 +3241,7 @@ constructSharedTransaction
balancedTx
, coinSelection =
mkApiCoinSelection deposits refunds
delCertsWithPath md
delCertsWithPath Nothing md
(unsignedTx outs apiDecoded pathForWithdrawal)
, fee = apiDecoded ^. #fee
}
Expand Down Expand Up @@ -4165,7 +4166,7 @@ mkApiWalletMigrationPlan s addresses rewardWithdrawal plan =
& view #selections
& F.foldMap (view #rewardWithdrawal)

mkApiCoinSelectionForMigration = mkApiCoinSelection [] [] Nothing Nothing
mkApiCoinSelectionForMigration = mkApiCoinSelection [] [] Nothing Nothing Nothing

mkApiWalletMigrationBalance :: TokenBundle -> ApiWalletMigrationBalance
mkApiWalletMigrationBalance b = ApiWalletMigrationBalance
Expand Down Expand Up @@ -4578,10 +4579,11 @@ mkApiCoinSelection
=> [Coin]
-> [Coin]
-> Maybe (DelegationAction, NonEmpty DerivationIndex)
-> Maybe (VotingAction, NonEmpty DerivationIndex)
-> Maybe W.TxMetadata
-> UnsignedTx input output change withdrawal
-> ApiCoinSelection n
mkApiCoinSelection deps refunds mcerts metadata unsignedTx =
mkApiCoinSelection deps refunds mDelCerts mVotingCerts metadata unsignedTx =
ApiCoinSelection
{ inputs = mkApiCoinSelectionInput
<$> unsignedTx ^. #unsignedInputs
Expand All @@ -4593,21 +4595,29 @@ mkApiCoinSelection deps refunds mcerts metadata unsignedTx =
<$> unsignedTx ^. #unsignedCollateral
, withdrawals = mkApiCoinSelectionWithdrawal
<$> unsignedTx ^. #unsignedWithdrawals
, certificates = uncurry mkApiCoinSelectionCerts
<$> mcerts
, certificates =
mergeCerts delCertsM votingCertsM
, depositsTaken = ApiAmount.fromCoin
<$> deps
, depositsReturned = ApiAmount.fromCoin
<$> refunds
, metadata = ApiBytesT. serialiseToCBOR
<$> metadata
}

mkApiCoinSelectionCerts
where
delCertsM = uncurry mkApiCoinSelectionDelCerts <$> mDelCerts
votingCertsM = uncurry mkApiCoinSelectionVotingCerts <$> mVotingCerts
mergeCerts del vote = case (del, vote) of
((Just dels), (Just votes)) -> Just $ NE.nub $ dels <> votes
(delM@(Just _), Nothing) -> delM
(Nothing, voteM@(Just _)) -> voteM
(Nothing, Nothing) -> Nothing

mkApiCoinSelectionDelCerts
:: DelegationAction
-> NonEmpty DerivationIndex
-> NonEmpty Api.ApiCertificate
mkApiCoinSelectionCerts action ixs =
mkApiCoinSelectionDelCerts action ixs =
case action of
Join pid -> pure $ Api.JoinPool apiStakePath (ApiT pid)
Quit -> pure $ Api.QuitPool apiStakePath
Expand All @@ -4618,6 +4628,20 @@ mkApiCoinSelectionCerts action ixs =
where
apiStakePath = ApiT <$> ixs

mkApiCoinSelectionVotingCerts
:: VotingAction
-> NonEmpty DerivationIndex
-> NonEmpty Api.ApiCertificate
mkApiCoinSelectionVotingCerts action ixs =
case action of
Vote drep -> pure $ Api.CastVote apiStakePath (ApiT drep)
VoteRegisteringKey drep -> NE.fromList
[ Api.RegisterRewardAccount apiStakePath
, Api.CastVote apiStakePath (ApiT drep)
]
where
apiStakePath = ApiT <$> ixs

mkApiCoinSelectionInput
:: forall n
. (TxIn, TxOut, NonEmpty DerivationIndex)
Expand Down
107 changes: 65 additions & 42 deletions lib/integration/scenarios/Test/Integration/Scenario/API/Voting.hs
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,16 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx1 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley src) Default delegationJoin

let registerStakeKeyCert = RegisterRewardAccount stakeKeyDerPath
let delegatingCert = JoinPool stakeKeyDerPath (ApiT pool1)

verify rTx1
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [depositAmt])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (registerStakeKeyCert NE.:| [delegatingCert]))
]

let ApiSerialisedTransaction apiTx1 _ = getFromResponse #transaction rTx1
Expand Down Expand Up @@ -310,24 +316,26 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx2 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley src) Default voteNoConfidence

let voting1 = ApiT NoConfidence
let votingCert1 = CastVote stakeKeyDerPath voting1

verify rTx2
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (votingCert1 NE.:| []))
]
let ApiSerialisedTransaction apiTx2 _ = getFromResponse #transaction rTx2
signedTx2 <- signTx ctx src apiTx2 [ expectResponseCode HTTP.status202 ]

let voting1 = ApiT NoConfidence
let votingCert1 =
WalletDelegationCertificate $ CastVote stakeKeyDerPath voting1

let decodePayload2 = Json (toJSON signedTx2)
rDecodedTx2 <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley src) Default decodePayload2
verify rDecodedTx2
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [votingCert1])
, expectField #certificates (`shouldBe` [WalletDelegationCertificate votingCert1])
, expectField #depositsTaken (`shouldBe` [])
, expectField #depositsReturned (`shouldBe` [])
]
Expand Down Expand Up @@ -373,25 +381,27 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx3 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley src) Default voteAbstain

let voting2 = ApiT Abstain
let votingCert2 = CastVote stakeKeyDerPath voting2

verify rTx3
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (votingCert2 NE.:| []))
]

let ApiSerialisedTransaction apiTx3 _ = getFromResponse #transaction rTx3
signedTx3 <- signTx ctx src apiTx3 [ expectResponseCode HTTP.status202 ]

let voting2 = ApiT Abstain
let votingCert2 =
WalletDelegationCertificate $ CastVote stakeKeyDerPath voting2

let decodePayload3 = Json (toJSON signedTx3)
rDecodedTx3 <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley src) Default decodePayload3
verify rDecodedTx3
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [votingCert2])
, expectField #certificates (`shouldBe` [WalletDelegationCertificate votingCert2])
, expectField #depositsTaken (`shouldBe` [])
, expectField #depositsReturned (`shouldBe` [])
]
Expand Down Expand Up @@ -485,29 +495,30 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx1 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley src) Default delegationJoinAbstain

let voting1 = ApiT Abstain
let votingCert1 = CastVote stakeKeyDerPath voting1
let registerStakeKeyCert = RegisterRewardAccount stakeKeyDerPath
let delegatingCert1 = JoinPool stakeKeyDerPath (ApiT pool1)

verify rTx1
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [depositAmt])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (registerStakeKeyCert NE.:| [delegatingCert1, votingCert1]))
]

let ApiSerialisedTransaction apiTx1 _ = getFromResponse #transaction rTx1
signedTx1 <- signTx ctx src apiTx1 [ expectResponseCode HTTP.status202 ]

let voting1 = ApiT Abstain
let votingCert1 =
WalletDelegationCertificate $ CastVote stakeKeyDerPath voting1
let registerStakeKeyCert =
WalletDelegationCertificate $ RegisterRewardAccount stakeKeyDerPath
let delegatingCert1 =
WalletDelegationCertificate $ JoinPool stakeKeyDerPath (ApiT pool1)

let decodePayload1 = Json (toJSON signedTx1)
rDecodedTx1 <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley src) Default decodePayload1
verify rDecodedTx1
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [registerStakeKeyCert, delegatingCert1, votingCert1])
, expectField #certificates
(`shouldBe` WalletDelegationCertificate <$> [registerStakeKeyCert, delegatingCert1, votingCert1])
, expectField #depositsTaken (`shouldBe` [depositAmt])
, expectField #depositsReturned (`shouldBe` [])
]
Expand Down Expand Up @@ -561,27 +572,29 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx2 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley src) Default delegationJoinNoConfidence

let voting2 = ApiT NoConfidence
let votingCert2 = CastVote stakeKeyDerPath voting2
let delegatingCert2 = JoinPool stakeKeyDerPath (ApiT pool2)

verify rTx2
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (delegatingCert2 NE.:| [votingCert2]))
]

let ApiSerialisedTransaction apiTx2 _ = getFromResponse #transaction rTx2
signedTx2 <- signTx ctx src apiTx2 [ expectResponseCode HTTP.status202 ]

let voting2 = ApiT NoConfidence
let votingCert2 =
WalletDelegationCertificate $ CastVote stakeKeyDerPath voting2
let delegatingCert2 =
WalletDelegationCertificate $ JoinPool stakeKeyDerPath (ApiT pool2)

let decodePayload2 = Json (toJSON signedTx2)
rDecodedTx2 <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley src) Default decodePayload2
verify rDecodedTx2
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [delegatingCert2, votingCert2])
, expectField #certificates
(`shouldBe` WalletDelegationCertificate <$> [delegatingCert2, votingCert2])
, expectField #depositsTaken (`shouldBe` [])
, expectField #depositsReturned (`shouldBe` [])
]
Expand Down Expand Up @@ -1111,22 +1124,24 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley wal) Default delegationQuit

let quittingCert = QuitPool stakeKeyDerPath
verify rTx
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [depositAmt])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (quittingCert NE.:| []))
]
let ApiSerialisedTransaction apiTx _ = getFromResponse #transaction rTx
signedTx <- signTx ctx wal apiTx [ expectResponseCode HTTP.status202 ]
let quittingCert =
WalletDelegationCertificate $ QuitPool stakeKeyDerPath

let decodePayload = Json (toJSON signedTx)
rDecodedTx <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley wal) Default decodePayload
verify rDecodedTx
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [quittingCert])
, expectField #certificates (`shouldBe` [WalletDelegationCertificate quittingCert])
, expectField #depositsReturned (`shouldBe` [depositAmt])
, expectField #depositsTaken (`shouldBe` [])
]
Expand Down Expand Up @@ -1158,28 +1173,33 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx1 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley wal) Default voteNoConfidence

-- as we are joining for the first time we expect two certificates
let registerStakeKeyCert =
RegisterRewardAccount stakeKeyDerPath
let voting1 = ApiT NoConfidence
let votingCert1 =
CastVote stakeKeyDerPath voting1

verify rTx1
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [depositAmt])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (registerStakeKeyCert NE.:| [votingCert1]))
]

let ApiSerialisedTransaction apiTx1 _ = getFromResponse #transaction rTx1
signedTx1 <- signTx ctx wal apiTx1 [ expectResponseCode HTTP.status202 ]

-- as we are joining for the first time we expect two certificates
let registerStakeKeyCert =
WalletDelegationCertificate $ RegisterRewardAccount stakeKeyDerPath
let voting1 = ApiT NoConfidence
let votingCert1 =
WalletDelegationCertificate $ CastVote stakeKeyDerPath voting1

let decodePayload1 = Json (toJSON signedTx1)
rDecodedTx1 <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley wal) Default decodePayload1
verify rDecodedTx1
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [registerStakeKeyCert, votingCert1])
, expectField #certificates
(`shouldBe` [ WalletDelegationCertificate registerStakeKeyCert
, WalletDelegationCertificate votingCert1])
, expectField #depositsTaken (`shouldBe` [depositAmt])
, expectField #depositsReturned (`shouldBe` [])
]
Expand Down Expand Up @@ -1227,25 +1247,28 @@ spec = describe "VOTING_TRANSACTIONS" $ do
}|]
rTx2 <- request @(ApiConstructTransaction n) ctx
(Link.createUnsignedTransaction @'Shelley wal) Default voteAbstain

let voting2 = ApiT Abstain
let votingCert2 = CastVote stakeKeyDerPath voting2

verify rTx2
[ expectResponseCode HTTP.status202
, expectField (#coinSelection . #depositsTaken) (`shouldBe` [])
, expectField (#coinSelection . #depositsReturned) (`shouldBe` [])
, expectField (#coinSelection . #certificates)
(`shouldBe` Just (votingCert2 NE.:| []))
]

let ApiSerialisedTransaction apiTx2 _ = getFromResponse #transaction rTx2
signedTx2 <- signTx ctx wal apiTx2 [ expectResponseCode HTTP.status202 ]

let voting2 = ApiT Abstain
let votingCert2 =
WalletDelegationCertificate $ CastVote stakeKeyDerPath voting2

let decodePayload2 = Json (toJSON signedTx2)
rDecodedTx2 <- request @(ApiDecodedTransaction n) ctx
(Link.decodeTransaction @'Shelley wal) Default decodePayload2
verify rDecodedTx2
[ expectResponseCode HTTP.status202
, expectField #certificates (`shouldBe` [votingCert2])
, expectField #certificates
(`shouldBe` [WalletDelegationCertificate votingCert2])
, expectField #depositsTaken (`shouldBe` [])
, expectField #depositsReturned (`shouldBe` [])
]
Expand Down

0 comments on commit 42b154c

Please sign in to comment.