-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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(bank): Allow injectable restrictions on bank transfers #14224
Conversation
…and give it similar stuff. Add unit tests for it.
…ing SendCoins to SendCoinsWithoutRestriction and create a new SendCoins that just applies the restriction then calls SendCoinsWithoutRestriction. Make restriction calls from InputOutputCoins too.
…ctions. It can be up to each restriction to decide if it should be bypassed.
…an be updated from inside the keeper without needing a pointer receiver.
…e the keeper AppendSendRestriction and PrependSendRestriction.
…estriction functions.
Very interesting capability ... these hooks can support everything from the send_disabled filters on tokens to rate limits on sends to/from accounts ... or even totals on token sent within a time period. It seems important that the send restrictions are implemented at the lowest level of balance update apis/logic within the bank module. There have been several security vulnerabilities reported due to the send_disabled logic being implemented at a higher level in the msg_server and relying on all other integrations with the bank module send keeper (such as IBC, wasmd) to also copy this restriction logic when making use of the lower level send APIs on the bank keeper. Moving to this approach would close a significant security risk avenue. |
…iction functions.
# Conflicts: # x/bank/keeper/send.go
[Cosmos SDK] Kudos, SonarCloud Quality Gate passed! |
I'm looking into a pretty different use case and found this issue. One question, how are you thinking about restriction of staking reward withdrawals? Because as of now I could delegate withdrawal capability using authz to a secondary account and then access staking rewards and transfer those freely. Marko was saying there might be some value in generalizing this approach in order to place restrictions on other modules. |
These new restrictions are applied at the lowest public level of the keeper functions. i.e. Restrictions in a module are going to be specific to the business of that module; their location and purpose are going to be different. As such, the data needed to apply a restriction is also going to be dependent on the module and what's being restricted. The location and purpose of each restriction is going to be different, and I'm not sure a generalized solution is desirable. Some general guidelines might be helpful, but that's as far as I'd go. In this PR, I designed the restrictions as something provided to the keeper after the keeper has been created. The bank keeper is needed by almost all other modules, so requiring the a restriction-providing keeper to be defined before it is painful and probably not even possible in most cases. If this were a less-core module, I probably would have opted for providing them as arguments to the keeper constructor. I also designed them to be additive, so that multiple restrictions can be provided from sources that have no knowledge of each other. I have three on-deck uses of these send restrictions. Two of them are restrictions on the receiver, but also need to know the sender; the third only needs to know the sender. Neither of the first two would try to prevent a send, only divert the send, which is why I made the The bank module already has the ability to inject a restriction on minting. It's use and the way it's provided to the module are different from what I've added though. The All that being said, I'm hoping that the |
… restiction check in SendCoins.
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
# Conflicts: # CHANGELOG.md
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.
LGTM, thank you @SpicyLemon!
# Conflicts: # CHANGELOG.md
Is there any chance to include this in |
# Conflicts: # CHANGELOG.md
@@ -22,7 +22,11 @@ import ( | |||
type SendKeeper interface { | |||
ViewKeeper | |||
|
|||
InputOutputCoins(ctx context.Context, inputs types.Input, outputs []types.Output) error | |||
AppendSendRestriction(restriction types.SendRestrictionFn) |
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.
Are send restrictions meant to be updated as the chain is running? Because I think having them initted once reduces API surface.
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.
They're not meant to be updated as the chain is running. The reasons they're appended/prepended like this are:
- The bank keeper can be created as one of the first (crucial since several other modules depend on it). It's expected that modules that need to inject send restrictions will often also need other bank keeper functionality.
- It allows other modules to inject their own restrictions (e.g. in their own keeper constructors) without needing knowledge of any other modules that have injected/will inject restrictions.
- Modules that need to inject a restriction won't work without that restriction being applied. It should be up to that module to make sure the restriction is injected. It shouldn't be relegated to something a blockchain author needs to know to wire up outside the module. If the restriction can be initialized only once, you'd have to create all the keepers, know which ones have restriction to apply, combine them, and give them and init them.
For example, we (Provenance) have three modules with send restrictions: sanction, quarantine, and marker. None of those modules work correctly without those send restrictions being applied. Because they're injected into the bank module during their own keeper creation, we can be certain that they're being applied.
Also, because they're being injected during their own keeper creation, they can each have unit tests in their own module that make sure they're being applied. If they had to be initialized once all together, those tests would have to live live at an app level.
Basically, this design fixes some pain points I encountered by having multiple modules with gov hooks.
(cherry picked from commit ff2e5a2) # Conflicts: # CHANGELOG.md
AppendSendRestriction(restriction SendRestrictionFn) | ||
PrependSendRestriction(restriction SendRestrictionFn) |
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.
This is late, and may have been discussed already, but I wonder what the need is for having 2 new methods, compared to just, say, AppendSendRestriction
? More importantly, how can a module decide whether its SendRestrictionFn
should be prepended vs appended? It seems like a global decision that can't be made locally.
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.
The ordering of send restrictions might matter. I expect most things will use Append
, but there could easily be a situation where a module needs theirs to go before the ones already injected.
I hate when I get into a situation where I just can't do what I know I need to do because the only layer I have access too didn't provide enough control. So, while Append
will usually be good enough, Prepend
and Clear
can easily save the day.
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.
could easily be a situation where a module needs theirs to go before the ones already injected.
But how does the module know whether it's safe to insert its restriction function before all others? That is, I see a problem where modules can be fighting each other for the correct position of their restrictions.
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.
The module can't know. The blockchain author can though, and needs to have the tools available to fix ordering problems if the arise.
SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error | ||
AppendSendRestriction(restriction SendRestrictionFn) | ||
PrependSendRestriction(restriction SendRestrictionFn) | ||
ClearSendRestriction() |
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.
When is this needed? If you've added restrictions, do you ever want to remove them again?
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.
Clear()
is needed for situations where a blockchain author needs to change the ordering of restrictions, and can't do so by other means.
Can we include this feature in v0.46? |
we are unable to backport this, it does have breaking changes |
Description
Closes: #14124
This PR provides a mechanism for injecting functions that can restrict bank transfers.
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...
!
to the type prefix if API or client breaking changeCHANGELOG.md
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...
!
in the type prefix if API or client breaking change