-
Notifications
You must be signed in to change notification settings - Fork 164
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
FLIP for publish
and claim
for capabilities
#1122
Conversation
Maybe allowing claim to both accounts (sender and receiver) can be good. ( if I publish to wrong address etc ) Also will this dictionary be available to user ? Some kind of seeing what I published, when I published can be good. If I published you something 1 month ago, if you didn't claim I want to be able to clean probably. |
Also I think it will be more familiar if we somehow use PS: unless we will have some kind of listing for potential things I can claim. But then we are becoming full inbox. |
|
||
transaction() { | ||
prepare(acct: AuthAccount) { | ||
let cap = acct.claim<Capability<MyIntf>>("yourCapability", provider: 0x1) |
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.
Have you considered a conveniene method to save the claim to a path directly? As that is what most people would want to do?
The `publish` function takes a `value` argument, a `name` string that identifies it, | ||
and a `recipient` address that specifies which account should be allowed to `claim` the | ||
published `value` later. When `publish` is called, `value` and its intended `recipient` are stored | ||
in a publishing dictionary on the calling account (not accessible to users), with the `name` as its key. |
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.
How do I know what I can claim onChain if I cannot access it? Maybe add a method to be able to list what is there for you?
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.
It might also make sense to emit an event when something is initially added to be claimed as well? Or is that considered out of scope? Seems like something folks would want to track:
- When a new capability or resource is claimable
- When it is claimed
- If the claimable was cancelled
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.
How do I know what I can claim onChain if I cannot access it?
I'm not sure how valid this question is in the context of bootstrapping. When A is trying to give a capability specifically to B, does B really need on-chain information to help B claim the capability?
A probably doesn't need to package something for a random account that it doesn't have an off-chain connection to, or is there a use case I missing?
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.
Another thing to add here is that if events need to be emitted, the publishing party can always write a wrapper function in a smart contract, that publishes and emits an event.
On the other hand, there is no way for the publisher to react when the value has been claimed. Maybe when publishing you could specify what happens when claiming? Some sort of callback. (that callback could emit an event or remove the published value)
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.
Coming back to this
Another thing to add here is that if events need to be emitted, the publishing party can always write a wrapper function in a smart contract, that publishes and emits an event.
Are you saying that events aren't necessary? Little confused by this. I specifically would advocate for a standardized event no matter what, the publisher should not be able to opt-out of that
On the other hand, there is no way for the publisher to react when the value has been claimed. Maybe when publishing you could specify what happens when claiming? Some sort of callback. (that callback could emit an event or remove the published value)
A callback makes sense to me, otherwise the publisher would need to also listen to events and react to them in a separate transaction which doesn't feel right to me
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.
Now we've settled on limiting this to just publishing/claiming capabilities, is a callback still necessary? claim
ing a value automatically removes the publishing entry, so the publisher would not need to do that themselves. What is the use case (specifically as part of capability bootstrapping) for a callback here?
At the moment the account calling the |
Thanks, makes sense |
Would it make sense to have a third function to revoke/cancel a claimable item as well? Or would the assumption be that you would instead just destroy the underlying capability/resource being claimed which would mean it can't happen anyway |
Thanks for the quick comments on this! Based on the discussion so far, it seems like we need two more additional pieces of functionality here:
|
In the events discussion, problem here is: this is privileged function ( claim ), so we cannot use it as a building block, enrich with contracts, etc. So events are for sure important, but also I think some kind of on-chain query would be also nice. At least some list of addresses, which I can claim something ( like a notification ) |
I added an |
it does not match, then `claim` returns `nil`. If it does match, `value` is removed from the `provider`'s dictionary and | ||
returned to the `claim` calling account. In effect, this means that a `publish`ed value can only be `claim`ed once. |
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.
value can only be
claim
ed once
Whats the reason for this? Is there any issue with claiming the same capability multiple times. why not just leave it.
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 would allow the API to support publishing/claiming resources easily. If we decide that we only want this to work for capabilities we could discuss removing this limitation.
I'm very supportive of this FLIP! It's a major improvement over the identity object proposal, and it doesn't encourage the msg.sender pattern. However, I think it introduces a major issue with spam and other abuse. Spam and AbuseIn capability-based security, a lot of the security comes from the fact that everyone is disconnected from each other until they explicitly consent to connect (and that connection can be revoked.) This FLIP proposes to change that. Now everyone has a direct, irrevocable connection to everyone else. Granted, there are some protections: the Provider is paying all the storage costs for what they are sharing. However, relying on storage costs alone to prevent spam and abuse will break as soon as the incentive for abuse > storage costs. For instance, imagine a motivated and coordinated harassment attack, where the Recipient keeps getting notifications where the string SolutionsAn easy solution would be to add a I wonder, since this FLIP is supposed to only be about solving the bootstrap problem, if the publish/claim API should be much more limited. Specifically, the Recipient could publish only one capability per Provider. And that capability would be of a particular type: a private inbox. In this case, there's no need for a name that can be used to transmit abusive messages from the general public. Here's how I imagine Dete's example use case would work:
As another use case, let's imagine that rather than Dete, the sender is someone that Bjarte doesn't trust much:
Complications and other thoughts
At this point, I think we definitely need to solve the spam/abuse problem, and I would love to find a way to do that using the private inbox pattern, since that is the traditional capability pattern for this. However, the above complications might make that infeasible. Still, any step towards a private inbox model, especially allowing for attenuation (rules about who can send what when), might be a step in the right direction. |
@katelynsills thank you for the detailed comment! I hadn't considered the potential for spam and abuse when designing the API, so I appreciate that you pointed it out here. Based on my understanding of your analysis (and please correct me if I am wrong here!), the primary issue with the API currently is that it allows a Provider to send something to any Recipient, without the Recipient's explicit consent. One potential option would be to reverse the responsibility relationship for who initiates the connection: instead of Another solution to this would be the inverse of the This seems like a limited version of the private inbox paradigm you are suggesting, that also addresses the additional complications you mentioned: because the Recipient must still explicitly Let me know your thoughts on this! It's very possible I have missed some more cases here. |
Do you mean that the sender is able to claim on behalf of the receiver, so they could unpublish the capability, e.g. to clean up?
Agreed! Some form of on-chain introspection would be great. We already have it for some on-chain data (e.g. storage iteration) and are currently adding it for other data (e.g. account keys) where it is missing.
The |
yeah but @dsainati1 already added |
Good point! At first I assumed that such a system, given that it is a mailbox, would be similar to e.g. snail and electronic mail, which are opt-in by default. However, given that this FLIP proposes a system for a very specific and limited purpose, bootstrapping capabilities, requiring explicit permission from the receiver for the sender to publish something seems reasonable 👍 |
Ah, I didn't saw that yet. An |
I don't think this is the intended use case for this feature; if you want to send people tokens, for example, we'd much rather encourage you to use the existing |
I've been assuming we were trying to solve the bootstrapping problem that Dete outlined. The user problem is: you want to give a capability to someone else, and you don't want to give anyone other than the Recipient access to the capability. You have no prior connection to the Recipient. The current solutions have major flaws:
Here's how Dete described the scenario:
The reason why Receivers don't work here is that they themselves fall prey to the bootstrapping problem. How does Bjarte give the receiver (a capability) specifically to Dete, and not anyone else? It's the same problem. This FLIP really does a great job at solving this hard problem, in a way that doesn't get developers used to depending on msg.sender. |
@katelynsills I think I explained this above like this: ( this is what @bjartek refers as Capability Receiver pattern )
Problem with Even |
Ah, good point @bluesign, thanks for the reminder!
But there is an important difference, right? We don't want smart contract developers to be writing code or using code at the smart contract level that is gated on identity. That doesn't allow delegation, and it will lead developers to not understand what capabilities can do that ACLs can't. If it's understood that |
Yeah but if we limit this too much and not provide a benefit, people will continue using capability receiver and implement their ACL on top of that. We need to beat capability receiver pattern, to get usage and acceptance from developers. If we give something super restricted like this, I am afraid they will not use basically.
Some analogy, we want people to drive slow, and then offering them slower car with worse interior. |
@bluesign, that's a really good reminder that just saying "no" to developers isn't a good strategy. I'd be interested in your thoughts on how to engage other developers in seeing the really cool things that capabilities & safe, permissionless delegation can do. This FLIP seems like the wrong place for it, but that kind of discussion seems very valuable. I think allowing capabilities to have behaviors themselves and expanding beyond the facet pattern would help, but that's also outside the scope of this FLIP. To continue your analogy, I think we should enforce a speed limit when we know it's a dead-end and there's a brick wall ahead (otherwise all our drivers are going to die in a fiery crash), but we also should tell the drivers that there's a high speed train right nearby that will let them go faster than any car would. And if the high speed train isn't built yet, we need to work on getting there :) |
How do we currently do bootstrapping? We use Capability Receivers. A short rundown of that (with S as the Sender a.k.a.: the flow account that wants to send/give a capability, and R the Receiver the flow account that is to receive this capability):
What are the problems:
What I think the solution is: The original proposal had just These functions change the bootstrapping pattern to:
Much simpler!
At this point there were questions about spamming and weather or not to send a common event when something is published. Can R be spammed, by constantly publishing for R? I don't think so. So in short I think something like this would be sufficient (I probably got the generic notation wrong) (these are all on AuthAccount):
(I don't see the argument for needing to permit before something can be published (ab)use for other things than bootstrapping I don't think this is a very efficient way of sending things, especially if we limit this to only capabilities. The reason is that S must notify R off-chain that something is available to be picked up. If S and R intend to exchange a lot more stuff (than just one capability) maybe instead of going through bootstrapping each time, they should instead bootstrap a (pull) channel where S gives the capability to pull from the channel to R and for further sharing S just pushes more stuff to the channel. How that channel looks like is up to S and R, but I would imagine that S would want to emit an on-chain event that R knows about when adding to the channel. Some other (random) thoughts:
|
If we decide for sure that no events are emitted, and the potential spam doesn't clog up block explorer displays, it makes sense to me to leave out |
I do not agree on this. I think a shared event should be emitted. Why is this "spamming" a problem here? Or is this something you claim? The benefits of emiting a shared event is numerous! In .find we could listen to 1 event to know if a user has anything published to them, and then prompt them to accept it, or flow port could do the same. Shared events also signal that an mutation has happend on chain and that is a documented best practise. We should use more shared events, not less. Visbility is important. An alternative to a shared event that I could live with is if it was possible to ask onChain if I have something I can claim. |
I feel significantly less concerned about this capability spam than you seem to be. This is not equivilent to token spam in ethereum because it doesn't create the same attack surface where a user further gives approval to their whole account to a malicious contract. Critically, the attack that's most concerning is actually having a token in your possession that looks valuable but is malicious. If massive spam is a problem then it would mean that spammers would just be making themselves less visible, since there would be lots of noise in block explorers. Even on networks with low fees, I have public accounts that have received some token spam but it's not THAT high. In this case the EV of the attack is so low that id be willing to ignore it so long as there's significant dev advantage to having a standard event. |
Even if we decide to not emit events, I still think allowing an open connection between any two accounts by means of this API does still pose an issue that @katelynsills mentioned earlier:
If we don't want to require an initial |
Does this actually need to be a protocol level concern? Seems this type of blocking would be an application feature. |
The accounts are not connected. The only possible on chain connection they have, that would allow for abusive messaging is the common event emitted during publishing. I imagine that event would contain:
If this event would exists I imagine people would want to listen to anything that was published for them, which opens them to receive spam (with a custom message in the name field). @bjartek @pgebheim I still feel that there should not be a common (cadence-level) event emitted when publishing. But this is the only argument I have. I can perhaps suggest that we add the feature without the common event and open a separate discussion/PR for the common event. |
In my eyes this is a non-issue. There are numerous other ways a user can be spammed today. How many active users do not have a FLOAT account? A float user can be airdropped any NFT per the NFT standard. In Lost-And-Found you can add any resource or an NFT and send to a user and add a message. I have several more use cases where on chain "spam" is inevitable. We need a common event or a way to onChain know if a user has something to claim for them. |
For what it's worth, here's a real life example of the kind of abuse and harassment that I'm concerned about. Specifically, it's the name field that would be used to transmit a hateful message. An attack like this would prevent the recipient from being able to be use Flow at all. One option that I haven't seen discussed yet is to keep the default open (anyone can publish to anyone, and every publish emits an event) but allow users to turn on an "Advanced Protection" mode in which they only accept published capabilities from accounts that they explicitly permit access. Seems like that could be the best of both worlds in terms of usability and security. |
@katelynsills that is a really good example, and my point is that doing that is very easy already. And doing so in tools that is way easier available then this solution that is something that most likely only very technically savvy users/developers will user either way. |
I am not convinced that this is a particularly compelling argument; just because a bad practice is commonplace already does not excuse us as designers if we contribute to the problem by adding another vector for harassment. |
We had a public meeting about this FLIP today (Sept 26) and we settled on a few specific steps forward here:
|
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! Just a couple questions regarding naming (minor)
Co-authored-by: Bastian Müller <bastian@axiomzen.co>
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.
Nice!
Adds a new proposal for a pair of API functions:
publish
andclaim
that are designed to address the capability bootstrapping use case detailed here onflow/cadence#1951For contributor use:
master
branchFiles changed
in the Github PR explorer