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

feat: clawback vesting accounts can return grants to the funder #342

Merged
merged 9 commits into from
Dec 5, 2023

Conversation

JimLarson
Copy link

@JimLarson JimLarson commented Dec 4, 2023

Description

Refs: Agoric/agoric-sdk#8570

Adds a message type that allows ClawbackVestingAccounts to return the entire vesting grant to the funder. Unlike clawback, it attempts to return both vested and unvested funds from the initial grant.

Note that this will require an additional PR in Agoric/agoric-sdk to modify the x/lien account wrapper to handle the extra method.

Reviewer: please check that I've added the new command and proto definitions to the appropriate registries. Compare to the clawback command/message.


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • added ! to the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • followed the guidelines for building modules
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • included comments for documenting Go code
  • updated the relevant documentation or specification
  • reviewed "Files changed" and left comments if necessary
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic
  • reviewed API design and naming
  • reviewed documentation is accurate
  • reviewed tests and test coverage
  • manually tested (if applicable)

The use of the "action" interfaces is to allow account wrappers
without creating cyclic package imports. But the mechanism only
allows a single sequence of tests, not an arbitrary graph.
Therefore we simplify (for now) that a ClawbackVestingAccount
is the only account type that supports return-grants. If we add,
say, PeriodicVestingAccount in the future, we'll add the ReturnGrants
method to the GrantAccount interface, etc.
@JimLarson JimLarson added enhancement New feature or request agoric-cosmos Agoric tags for cosmos labels Dec 4, 2023
@JimLarson JimLarson requested a review from michaelfig December 4, 2023 16:00
@JimLarson JimLarson self-assigned this Dec 4, 2023
Copy link
Collaborator

@michaelfig michaelfig left a comment

Choose a reason for hiding this comment

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

Please address or rebut comments. LGTM overall!

Comment on lines +8740 to +8741
MsgReturnGrants defines a message for a grantee to return the unvested portion of a
vesting grant to the funder. Currently only applies to ClawbackVesting accounts.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please update for current algorithm.

Suggested change
MsgReturnGrants defines a message for a grantee to return the unvested portion of a
vesting grant to the funder. Currently only applies to ClawbackVesting accounts.
MsgReturnGrants defines a message for a grantee to return all granted assets,
including delegated, undelegated and unbonding, vested and unvested,
are transferred to the original funder of the account. Might not be complete if
some vested assets have been transferred out of the account. Currently only applies to
ClawbackVesting accounts.

Copy link
Author

Choose a reason for hiding this comment

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

This is a generated file, but I made the requested change upstream in proto/cosmos/vesting/v1beta1/tx.proto.

@@ -17,6 +17,9 @@ const (

// TypeMsgClawback defines the type value for a MsgClawback.
TypeMsgClawback = "msg_clawback"

// TypeMsgClawback defines the type value for a MsgClawback.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// TypeMsgClawback defines the type value for a MsgClawback.
// TypeMsgReturnGrants defines the type value for a MsgReturnGrants.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

// validator has been removed
continue
}
wantShares, err := validator.SharesFromTokensTruncated(want)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What happens if the delegation has fewer shares than the corresponding want? Does wantShares get reduced here, or in the assignment to transferredShares below?

A clarifying comment would be appreciated.

Copy link
Author

Choose a reason for hiding this comment

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

TransferDelegation() might transfer fewer shares than requested - added a comment.


// Reduce DelegatedFree/Vesting by actual unbonding/staked amount transferred.
// The distinction between DF and DV is not significant in practice - only the
// sum in meaningful. Furthermore, DF/DV is not updated
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// sum in meaningful. Furthermore, DF/DV is not updated
// sum is meaningful. Furthermore, DF/DV is not updated

Copy link
Author

Choose a reason for hiding this comment

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

Done - and completed the sentence fragment.

// gotCoins should be zero at this point, unless DF+DV was not accurate

// If we've transferred everything and still haven't transferred the desired clawback amount,
// then the account must have most some unvested tokens from slashing.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// then the account must have most some unvested tokens from slashing.
// then the account must have lost some unvested tokens from slashing.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

toXfer := coinsMin(amt, spendable)
err := bk.SendCoins(ctx, addr, dest, toXfer)
if err != nil {
return err // shouldn't happen, given spendable check
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should panic, for clarity that the vesting account state won't be changed on failure.

Suggested change
return err // shouldn't happen, given spendable check
panic(err) // shouldn't happen, given spendable check

Copy link
Author

Choose a reason for hiding this comment

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

Done.

// Any unbonding or staked tokens transferred are deducted from
// the DelegatedFree/Vesting fields.
// TODO: refactor to de-dup with clawback()
func (va *BaseVestingAccount) forceTransfer(ctx sdk.Context, amt sdk.Coins, dest sdk.AccAddress, ak AccountKeeper, bk BankKeeper, sk StakingKeeper) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this function fails, it will leave the keepers in an inconsistent state, so either:

  • don't return any errors, always panic on them here
  • always panic on returned error in the caller

A panic will roll back the transaction.

Copy link
Author

Choose a reason for hiding this comment

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

Done - removed the error return, here and in the parent caller too.

require.False(t, found)

// check destination account
// want 1000fee, 83stake (18 unbonded, 5 unboinding, 60 bonded)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// want 1000fee, 83stake (18 unbonded, 5 unboinding, 60 bonded)
// want 1000fee, 83stake (18 unbonded, 5 unbonding, 60 bonded)

require.Equal(t, sdk.NewInt(60), stakeAmt)
ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, dest, valAddr)
require.True(t, found)
require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance)
require.Equal(t, 1, len(ubd.Entries))
require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance)

Copy link
Author

Choose a reason for hiding this comment

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

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see require.Equal(t, 1, len(ubd.Entries)). Did you push it yet?

x/auth/vesting/client/cli/tx.go Show resolved Hide resolved
@JimLarson
Copy link
Author

Test failures appear to be unrelated to the content of the PR.

@ivanlei ivanlei linked an issue Dec 4, 2023 that may be closed by this pull request
require.Equal(t, sdk.NewInt(60), stakeAmt)
ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, dest, valAddr)
require.True(t, found)
require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see require.Equal(t, 1, len(ubd.Entries)). Did you push it yet?

@JimLarson
Copy link
Author

Turns out I hadn't pushed everything. I've definitely got to squash this at merge time to hide my shame.

@michaelfig
Copy link
Collaborator

BTW, as a drive-by, the following fixes the data race in coin_test.go causing the failure in the prior run. If you don't want to apply it, let's turn it into an issue.

diff --git a/types/coin_test.go b/types/coin_test.go
index 8dcd9b25dd..bdc1756328 100644
--- a/types/coin_test.go
+++ b/types/coin_test.go
@@ -21,8 +21,13 @@ type coinTestSuite struct {
 	ca0, ca1, ca2, cm0, cm1, cm2 sdk.Coin
 }
 
+type serialTestSuite struct {
+	suite.Suite
+}
+
 func TestCoinTestSuite(t *testing.T) {
 	suite.Run(t, new(coinTestSuite))
+	suite.Run(t, new(serialTestSuite))
 }
 
 func (s *coinTestSuite) SetupSuite() {
@@ -104,7 +109,8 @@ func (s *coinTestSuite) TestCoinIsValid() {
 	}
 }
 
-func (s *coinTestSuite) TestCustomValidation() {
+// TestCustomValidation needs to be serial because it mutates the CoinDenomRegex.
+func (s *serialTestSuite) TestCustomValidation() {
 	newDnmRegex := `[\x{1F600}-\x{1F6FF}]`
 	sdk.SetCoinDenomRegex(func() string {
 		return newDnmRegex

@JimLarson
Copy link
Author

Is the name serialTestSuite magic? I'll file an issue - we'll want to upstream this.

@JimLarson
Copy link
Author

Filed Agoric/agoric-sdk#8612 for the coins test. Still confused by the proposed solution.

@JimLarson JimLarson merged commit cd18323 into Agoric Dec 5, 2023
29 of 31 checks passed
@JimLarson JimLarson deleted the 8570-return-grants branch December 5, 2023 04:15
@michaelfig
Copy link
Collaborator

Is the name serialTestSuite magic? I'll file an issue - we'll want to upstream this.

serialTestSuite matches the struct at the top to the receiver of TestCustomValidation. The coinTestSuite runs all of its tests in parallel, which triggers the data race. Running serialTestSuite after coinTestSuite ensures the race can't happen.

@JimLarson
Copy link
Author

Ah, I get it. We're just isolating that test in its own suite, but within the same Go test.

JeancarloBarrios pushed a commit that referenced this pull request Sep 28, 2024
Adds a message type that allows ClawbackVestingAccounts to return the entire vesting grant to the funder. Unlike clawback, it attempts to return both vested and unvested funds from the initial grant.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
agoric-cosmos Agoric tags for cosmos C:x/auth enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow return grant of clawback vesting accounts
2 participants