Sponsor Reserves for Ledger Entries (CAP-0031)

325 views
Skip to first unread message

Jonathan Jove

unread,
Apr 27, 2020, 9:23:26 AM4/27/20
to Stellar Developers

Hey everyone,


There have been many requests for support in the Stellar protocol to pay the reserve for ledger entries from another account. Based on these requests, I have drafted https://github.com/stellar/stellar-protocol/blob/master/core/cap-0031.md which introduces a generic mechanism for this. To show what is possible, this proposal contains more than what I think would actually be included in the first version of an implementation. I am unsure, for example, about including offer sponsorship due to its complexity unless people see that it would be extremely valuable. 


The proposal looks very long, but most of it is just a detailed explanation of required changes to operations in order to do the new book-keeping.


I would appreciate any and all feedback. Love it? Hate it? Does/doesn’t meet your requirements? Do/don’t like the approach? Let me know.


Thanks,

Jon

Nicolas Barry

unread,
Apr 28, 2020, 9:55:19 PM4/28/20
to Jonathan Jove, Stellar Developers
Yeah that seems quite powerful.

Here are the notes I took while reviewing this proposal.

## Xdr

 

### AccountEntry

 

Should extend `v1()` instead of creating `v2()` see https://github.com/stellar/stellar-core/blob/master/docs/versioning.md#ledger-object-versioning

 

### CreateSponsorshipResult

 

Should probably return the sponsorship ID? Otherwise, how does the caller know the mapping?

 

### Missing LedgerHeader changes?

 

You're using  a global `sponsorshipID` generator that probably needs to be tracked in the ledger header; that or there needs to be a way to generate those IDs.

 

## Semantics

 

There should be an intro paragraph that in addition to the explanation on how we calculate the available balance, explains how sponsorship actually works: right now the reader has to go over each operation and figure it out.

 

In particular, this intro should allow to also implement invariants.

 

From what I understand:

 * creating sponsorships always succeeds (if the account has enough balance)

 * `sponsoredReserves` tracks currently sponsored entries, ie we always have `2 + numSubEntries - sponsoredReserves >= 0` (ie, we don't count redundant sponsors against `sponsoredReserves`).

    * corollary: when creating/removing ledger entries, we have to keep `sponsoredReserves` up to date

 * only one thing sponsored by sponsored entry

 

There are no mentions of how certain things work (and why), in particular:

 * is there a limit on the number of sponsors?

     * related, the constant use of `count sponsorships` seems to indicate to me that we need to store this information somewhere - so why not just add a LedgerEntry for this?

 * sponsorships should probably counted as subentries for the sponsor account, otherwise the account can be merged and this creates a bunch of new problems.

 * sponsorships cannot be deleted if "in use" - why?

    * a sponsor may only want to sponsor accounts "in good standing" (ie, they're actually using the trustline)

        * this is especially problematic as there is no 1:1 mapping between what is being sponsored and sponsor, so a sponsor may get stuck to sponsor an account that deleted what they were sponsoring.

 * should there be a way for accounts to disable sponsorships? As this changes the "available balance" of an account, this may cause operational problems to automated systems, in particular it may change the behavior of many operations, especially as the sponsor can remove its sponsorship at any time which causes the available balance to go down.

    * ~ related, why not allow the sponsored account to remove sponsorships attached to their account?

 * behavior wrt base reserve increase

       * when a sponsorship becomes "used", there is no check that compares the current base reserve to the one stored in the sponsorship entry - this can be problematic as it creates a (potentially large) incentive to create (potentially a lot) sponsorship entries ahead of a base reserve increase.

 * Any reason to not make references to assets in Trustline, offers optional as to match on anything? (this probably depends on relaxing the "in use" rule)

 * sponsoring of specific "Data" field doesn't seem super useful and is wasteful? Maybe first version should not have this (or make optional too?)

 * why not allow more than 1 sponsorship per entry? Signers, Data(?), Offers could benefit from a counter.

 

 

## Operations

 

General readability: when writing pseudo code, it would be easier to read if you were writing things in a more "standard way", right now you're using something that is a cross between Fortran (gotos everywhere) and Perl/Ruby (postfix conditions). I would use python-like pseudo code instead that makes great use of indentation, can include comments to label sections, and doesn't use "goto", for example:

 

```

# ensure that `sourceAccount` can pay for this

if the `sourceAccount` does not have at least `Multiplier * baseReserve` available balance of native asset:

    Fail with `CREATE_SPONSORSHIP_LOW_RESERVE`

```

 

 

### Creation

 

 

I don't think we should allow an account to sponsor themselves, it looks like you allow it.

 

You didn't specify when and how `sponsorshipID` gets generated (it seems it's just incremented).

 

 

Step 4: I wonder if we should just store `sponsorship.reserve = baseReserve` to avoid having to divide by the multiplier everywhere we use sponsorships.

 

Step 5 says "number of LedgerEntry" but that doesn't seem right, for signers, this number would be 1 but we want to sponsor based on the number of signers. So should be "number of subentries".

Also, for other types (like `OfferEntry`), this seems to be very inefficient.

 

Step 6, I don't understand why there is the condition on `sponsorshipID`. How can this condition be false as we're creating a new entry?

Also, how can this be done efficiently (maybe tracking this in a separate ledger entry solves this problem)?

 

Step 8..11 should have a comment that explains that we're sponsoring an existing entry.

 

 

### Other operations

 

I didn't review other operations in detail as there was already too many question on "Create", so here are only some questions.

 

#### removeSponsorshipOp

 

I don't see why we need the complexity from step 5 & 6:

 * 5 makes it impossible to transfer sponsorship

 * 6 makes it impossible to stop sponsoring something

 

I imagine that you're trying to preserve some mapping between groups (as described at the end of "How are sponsorship entries paired with the ledger entries they sponsor"); but this is not super useful as we don't even have the right properties even for a single sponsor:

If a sponsored account has 2 signers sponsored, it can easily create 2 new signers, and delete the ones that the sponsor wanted to sponsor.

 

So I would make all sponsors (for a given condition) fungible instead, that way you only need the count for that condition without a constraint on IDs.

 

## Semantics for offers

 

I don't think we should allow sponsoring of all types from the get go, in particular offers.

 

Having "sponsored offers" has no alignment with "cross border payment" and comes with overhead at basically every stage (ie it's not a "freebie"):

* offers get taken all the time, so this adds a lot of churn to the ledger.
      * It seems that this would have to come with higher fees somehow - but this would be a departure from having fees independent from the ledger state.
 * when sponsoring an offer as what is being sponsored is an asset pair, we'd have to match against all offers of the user (ie, load all offers for that user).
      * This seems to violate the argument on why we should not have sponsorship of multiple ledger entries, where "Why do sponsorship entries sponsor reserve for only a single ledger entry?" seems to say that we should not allow it.

 

#### CreateAccount

 

This applies to other existing operations.

I don't understand what "step 1" is referencing - maybe you're just trying to say we're replacing "this" with "that", but in that case, just write down what is being replaced.

 

For create account, it seems that we also need to set `sponsoredReserves` to `2` when we're creating a sponsored account?

 

 

#### Merge account

 

Merge needs to be modified so that we have the proper behavior if the account is sponsored and/or has sponsored signers.




--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/CAG4FKhycnUsUHcyjUg-5N4tDhvRMGeXWTgQAyxENjppruTnRJQ%40mail.gmail.com.

Leigh McCulloch

unread,
Apr 29, 2020, 1:16:12 AM4/29/20
to Stellar Developers
Hi Jon,

This is really exciting. A few thoughts I had, some that I shared directly with you but wanted to surface in this thread, and some that are new:

1. I think it is important that signer sponsorship be able to optionally specify the exact signer being sponsored. In some situations we know the signer and so we could sponsor only that exact signer and the removal of that signer and addition of another signer could free up the sponsorship. If we don't offer this it will be hard to free up a sponsored signer entry with the migration of an account out of one ecosystem and into another. If there's only one signer and someone wants to transition away they need to remove and add a signer at the same time. There's no way to free up the sponsorship.

2. I think there will be cases where sponsorship will be temporary. One example is the LOBSTR wallet which treats the minimum reserve given to its users as a loan which is required to be paid back and there is limited functionality within the app until that occurs. If a wallet or service uses this pattern and wants to use sponsorship is there a way for the sponsor to remove the sponsorship of the account once the user has added the necessary lumens themselves? I think it looks like that is possible but I'm unsure. If this is possible then it might make the need for a specific signer in (1) unnecessary.

3. It looks like the sponsored reserve gets deducted from the account of the sponsor. I think this will be confusing given that the existing reserves today stay in the account. Some wallets calculate and display the amount of the native balance is reserved on an account, I would have thought the reserve that is supporting other accounts would remain in the sponsors account but would be unusable. I also struggle to imagine how the balance of the account changing will be represented in balance sheets and statements. The adding and removing of sponsors will credit and debit the balance which I imagine will be present as effects in Horizon but the funds are still owned by the sponsor. An impact of this is that the sponsors account could be merged/deleted while there are active sponsors, but I think this is a good thing.

Leigh

Jonathan Jove

unread,
Apr 30, 2020, 12:06:07 PM4/30/20
to Leigh McCulloch, Stellar Developers
Thank you for all the feedback, there are a lot of good corrections in here. I want to focus on a few specific questions, because they are fundamental to the design of the proposal. Once we've settled these issues, I will address the other comments.

(From the "Semantics" section)

> sponsorships should probably counted as subentries for the sponsor account, otherwise the account can be merged and this creates a bunch of new problems.
It is actually the opposite: counting sponsorships as subentries creates problems for MergeOp. Specifically, there is no guarantee that you can ever get a sponsorship back (see "6 makes it impossible to stop sponsoring something" item 1). This means that if sponsorships are subentries, then it may be impossible to merge your account. You noted at the end of your comments that "Merge needs to be modified so that we have the proper behavior if the account is sponsored and/or has sponsored signers." Actually, everything just works correctly as is. Merging should move the entire balance from the source account into the destination account, but should not interact with the sponsorship entries because the point of sponsorship is that it does not allow the sponsored reserve to move. The destination account of a merge does not undergo any change in its subentries (eg. the signers do not merge) so it also does not interact with its sponsorships. Aside from all of this, making sponsorships not be subentries allows for an unlimited number of them. Remarks like this already exist in CAP-0023 and CAP-0032, and I will add them to CAP-0031.


> why not allow the sponsored account to remove sponsorships attached to their account?
A sponsorship is free money, since they cannot be removed when in use according to this proposal. As a consequence, there is no reason to ever remove them.


> when a sponsorship becomes "used", there is no check that compares the current base reserve to the one stored in the sponsorship entry - this can be problematic as it creates a (potentially large) incentive to create (potentially a lot) sponsorship entries ahead of a base reserve increase.
This is a really good point, but I don't know exactly what to do about it yet. The challenge is that we don't modify the sponsorship entries when they come into use, so there is no way to know if it had the right reserve at that time (imagine if the base reserve fluctuated up then back down). Do you have any ideas about how to avoid this situation?


> Any reason to not make references to assets in Trustline, offers optional as to match on anything? (this probably depends on relaxing the "in use" rule)
There is a very important technical reason for this. Determining the number of sponsored reserves is equivalent to solving an instance of bipartite https://en.wikipedia.org/wiki/Maximum_cardinality_matching so we have to guarantee that every possible instance can be solved efficiently. The sponsorship descriptors in my proposal make the solution trivial (the instances become so simple, it is hard to even consider it as maximum cardinality matching). Allowing optional constraints makes this much harder. It would be possible for trust lines and signers, which have only one "wild-card", but it would be basically impossible for offers as they have two. That being said, I don't see the value of an optional asset for trust lines. But as Leigh has pointed out, it could be valuable for signers to allow specifying the specific signer.


> why not allow more than 1 sponsorship per entry? Signers, Data(?), Offers could benefit from a counter.
I discussed in https://github.com/stellar/stellar-protocol/blob/master/core/cap-0031.md#sponsorship-entries-always-provide-a-full-reserve why I think that this adds complexity without adding much functionality. Do you disagree with the analysis there, or see some benefit that is not clear to me?

(From the "removeSponsorshipOp" section)

> 5 makes it impossible to transfer sponsorship
I don't really understand why you would want to transfer sponsorship. If you do not control both accounts, then why would the other party take on the cost of the sponsorship since? If you do control both accounts, then why does it matter which account is associated with the sponsorship? We could always add a TransferSponsorshipOp if we think this is actually useful.


> 6 makes it impossible to stop sponsoring something
There are several reasons why I think this is a feature, not a bug.
1. Any user of this feature who assumes that they will definitely be able to get their sponsorship back is wrong. It is clear that no proposal can allow a sponsorship to be removed if it would cause the account to have insufficient reserve (after accounting for other existing sponsorships). If this were permitted, then you could simple create a sponsorship, create the sponsored entry, then remove the sponsorship which would circumvent the reserve requirement entirely. So let's assume we have a proposal where it is possible to remove a sponsorship if the account would still have sufficient reserve. This implies that the account has a non-zero available balance of native asset at the time the sponsorship is removed. But an account that currently has no available balance of native asset can unilaterally prevent this by executing the following transaction: send 1 base reserve from another account, add an offer selling an asset issued by this account for (INT64_MAX - reserve) native asset at a price 1:1. The account now has no available limit of native asset so it cannot receive any, no available balance of native asset so no sponsorship can be removed, and the offer cannot be taken.
2. But perhaps you think the above example is an arcane edge case that won't really happen in practice. So what happens when an account does enter a state in which it is possible to remove a sponsorship? Well, every sponsor will attempt to remove their sponsorships (one per transaction). But this is a race: most transactions will fail, and the sponsors that submit more transactions are more likely to succeed. This is obviously wasteful, and the incentives are bad.
3. What about malicious actors? They could sponsor entries and strategically remove the sponsorships in order to make transactions fail. This would probably necessitate adding a "disable sponsorships" mechanism like you mentioned, which is unnecessary in my proposal as written because receiving a sponsorship is no different from receiving a payment.

Best,
Jon

--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.

Nicolas Barry

unread,
May 4, 2020, 9:58:11 PM5/4/20
to Jonathan Jove, Leigh McCulloch, Stellar Developers

Looks like you responded to Leigh's thread by accident

 

(From the "Semantics" section)

> > sponsorships should probably counted as subentries for the sponsor account, otherwise the account can be merged and this creates a bunch of new problems.

> It is actually the opposite: counting sponsorships as subentries creates problems for MergeOp. Specifically, there is no guarantee that you can ever get a sponsorship back (see "6 makes it impossible to stop sponsoring something" item 1). This means that if sponsorships are subentries, then it may be impossible to merge your account. You noted at the end of your comments that "Merge needs to be modified so that we have the proper behavior if the account is sponsored and/or has sponsored signers." Actually, everything just works correctly as is. Merging should move the entire balance from the source account into the destination account, but should not interact with the sponsorship entries because the point of sponsorship is that it does not allow the sponsored reserve to move. The destination account of a merge does not undergo any change in its subentries (eg. the signers do not merge) so it also does not interact with its sponsorships. Aside from all of this, making sponsorships not be subentries allows for an unlimited number of them. Remarks like this already exist in CAP-0023 and CAP-0032, and I will add them to CAP-0031.

 

I guess we can resolve this question later as it depends on overall behavior. I agree that with the proposal as written sponsorships are completely detached from accounts.

 

 

> > why not allow the sponsored account to remove sponsorships attached to their account?

> A sponsorship is free money, since they cannot be removed when in use according to this proposal. As a consequence, there is no reason to ever remove them.

 

This was a sub-question to the question "should there be a way for accounts to disable sponsorships?", without which it may cause unnecessary pain for accounts that don't expect to be sponsored (exchange accounts for example).

 

 

> > when a sponsorship becomes "used", there is no check that compares the current base reserve to the one stored in the sponsorship entry - this can be problematic as it creates a (potentially large) incentive to create (potentially a lot) sponsorship entries ahead of a base reserve increase.

> This is a really good point, but I don't know exactly what to do about it yet. The challenge is that we don't modify the sponsorship entries when they come into use, so there is no way to know if it had the right reserve at that time (imagine if the base reserve fluctuated up then back down). Do you have any ideas about how to avoid this situation?

 

We could shift to reasoning in terms of native contribution towards the reserve for specific sub entry types. Then the account just needs to cover the difference if needed. I think for this to work, we'd have to break down the base reserve calculation to something like (instead of a calculation based only on counts):

    * total_base_reserve = Sum over categories( count*BASE_RESERVE - min(count*BASE_RESERVE, amount_sponsored(category))) // where categories = sponsored_categories \union non_sponsored_category

 

Btw, what https://github.com/stellar/stellar-protocol/blob/master/core/cap-0031.md#sponsorship-entries-always-provide-a-full-reserve  is incompatible with this and is a justification for hoarding sponsorships to get around the base reserve increase.


Couple things here:

* I think it's the right behavior to expect users of that feature to "unstick" accounts that are now wedged because they don't have enough balance as it avoids creating weird incentives
* If the sponsorship amount is a balance, I think it allows users of that feature to "plan ahead" and sponsor whatever they want ahead of the actual increase (we would not default to base reserve when creating sponsorships)

 

> > Any reason to not make references to assets in Trustline, offers optional as to match on anything? (this probably depends on relaxing the "in use" rule)

> There is a very important technical reason for this. Determining the number of sponsored reserves is equivalent to solving an instance of bipartite https://en.wikipedia.org/wiki/Maximum_cardinality_matching so we have to guarantee that every possible instance can be solved efficiently. The sponsorship descriptors in my proposal make the solution trivial (the instances become so simple, it is hard to even consider it as maximum cardinality matching). Allowing optional constraints makes this much harder. It would be possible for trust lines and signers, which have only one "wild-card", but it would be basically impossible for offers as they have two. That being said, I don't see the value of an optional asset for trust lines. But as Leigh has pointed out, it could be valuable for signers to allow specifying the specific signer.

 

I was focusing on the question before going into implementation. In particular, if we drop offers from this proposal, it seems we can allow this for all types.

 

I guess if you don't agree, I'll turn it the other way: why should we support unbounded signer sponsorship?

 

> > why not allow more than 1 sponsorship per entry? Signers, Data(?), Offers could benefit from a counter.

> I discussed in https://github.com/stellar/stellar-protocol/blob/master/core/cap-0031.md#sponsorship-entries-always-provide-a-full-reserve why I think that this adds complexity without adding much functionality. Do you disagree with the analysis there, or see some benefit that is not clear to me?

 

I think you were trying to point to https://github.com/stellar/stellar-protocol/blob/master/core/cap-0031.md#why-do-sponsorship-entries-sponsor-reserve-for-only-a-single-ledger-entry ?

 

I guess the limitations derive from certain choices you made that are in flux (for example, if we use "sponsored reserve"). We can resolve this question later.

 

 

 

(From the "removeSponsorshipOp" section)

> > 5 makes it impossible to transfer sponsorship

> I don't really understand why you would want to transfer sponsorship. If you do not control both accounts, then why would the other party take on the cost of the sponsorship since? If you do control both accounts, then why does it matter which account is associated with the sponsorship? We could always add a TransferSponsorshipOp if we think this is actually useful.

 

This is a bit backward: there should be justification for any constraint that we put in the system otherwise we're placing limits on things based on how we think those will be used, potentially closing the door to interesting scenarios that we're not smart enough to predict.

 

Anyways, this is more evident for unbounded sponsorships like signers or offers:

Imagine an account that has a relationship with 2 exchanges, and each exchange provides sponsorships of "up to 2 offers" (or something like this). If the account only has 2 offers, I don't see a reason to stop either exchange from changing their contract to "up to 1 offer" or even stop sponsoring offers (if the account stops being a client of that exchange let's say).

 

 

> > 6 makes it impossible to stop sponsoring something

> There are several reasons why I think this is a feature, not a bug.

> 1. Any user of this feature who assumes that they will definitely be able to get their sponsorship back is wrong. It is clear that no proposal can allow a sponsorship to be removed if it would cause the account to have insufficient reserve (after accounting for other existing sponsorships). If this were permitted, then you could simple create a sponsorship, create the sponsored entry, then remove the sponsorship which would circumvent the reserve requirement entirely. So let's assume we have a proposal where it is possible to remove a sponsorship if the account would still have sufficient reserve. This implies that the account has a non-zero available balance of native asset at the time the sponsorship is removed. But an account that currently has no available balance of native asset can unilaterally prevent this by executing the following transaction: send 1 base reserve from another account, add an offer selling an asset issued by this account for (INT64_MAX - reserve) native asset at a price 1:1. The account now has no available limit of native asset so it cannot receive any, no available balance of native asset so no sponsorship can be removed, and the offer cannot be taken.

> 2. But perhaps you think the above example is an arcane edge case that won't really happen in practice. So what happens when an account does enter a state in which it is possible to remove a sponsorship? Well, every sponsor will attempt to remove their sponsorships (one per transaction). But this is a race: most transactions will fail, and the sponsors that submit more transactions are more likely to succeed. This is obviously wasteful, and the incentives are bad.

> 3. What about malicious actors? They could sponsor entries and strategically remove the sponsorships in order to make transactions fail. This would probably necessitate adding a "disable sponsorships" mechanism like you mentioned, which is unnecessary in my proposal as written because receiving a sponsorship is no different from receiving a payment.

 

 

Sorry, I am having a hard time understanding 1&2 in this context: you're saying we should not allow to stop sponsoring because we can't guarantee that it can be done (1) or that many sponsors would for some reason decide to stop sponsoring the same account (2). Neither of these seem to be deal breakers to me for having this functionality: (1) seems totally acceptable and (2) seems like an edge case not worth dealing with.

 

3 is interesting, and is actually a problem even without this property (so we may have to have that flag or some other mechanism anyways?):

* maybe not as obvious but if an attacker sponsors something ephemeral (like an offer), the impacted service (like a trading bot) would have to constantly load the balance of the account in case the attacker stops sponsoring that ephemeral entry between trades (causing the bot to trade more than it should)
* Same problem every time the account removes an entry that was sponsored (much less often, but this makes it even better from an attacker point of view as it's very unlikely to be handled).

 

Expanding on what "transactions may fail" means, an attacker can make certain operations (like native payments) fail during consensus, but also put the account in a "wedged" state (not enough balance to pay for fees), where it needs to be rescued by some other account (either via a payment of sponsorship).

 

The thing about 3 is that if the semantics are clear (sponsors can go away at any time if you have the balance), then the code client side can actually deal with this, so we end up with better handling of the edge cases as well (going back to what needs to be done here: flag or information on sponsorship that the client can use are candidates).



Nicolas Barry

unread,
May 5, 2020, 11:18:11 AM5/5/20
to Stellar Developers
Thinking about this some more:
I suspect most of the problems are a result of the support for offer like entries.

For example, I think that if we remove support for any sponsorship that can sponsor more than one thing (so remove offers support and require signers sponsorships to be specific), the issue of bad incentives when the base reserve changes is non existent.

Nicolas

Leigh McCulloch

unread,
May 5, 2020, 9:11:44 PM5/5/20
to Stellar Developers
require signers sponsorships to be specific

I think requiring a signer sponsor to be for a specific signing key would make it too difficult to use in some applications. It assumes that the system providing sponsorship always knows about the system that is providing the signing key. If the signing key is provided by some other system, and is randomly generated for that specific user it will complicate the process that a client goes through. 

As an example, for Vega's use of a SEP-30 Recoverysigner server we'd need to find out what the signing keys are for the user ahead of sponsoring, but the user interacts directly with the Recoverysigner server. This would complicate the process of sponsoring the signers retrieved when registering with Recoverysigner with interacting with the wallet server.

I was originally convinced that we would need specific signing keys but I'm becoming more convinced that requiring a specific signing key is unnecessary as long as this situation I mentioned earlier is solvable:
If there's only one signer and someone wants to transition away they need to remove and add a signer at the same time. There's no way to free up the sponsorship. 

Leigh
> To unsubscribe from this group and stop receiving emails from it, send an email to stell...@googlegroups.com.
>
> To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/e9707d27-e833-4011-bc2b-1bf585b570c7%40googlegroups.com.
>
>
>
>
>
>
> --
>
> You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
>
> To unsubscribe from this group and stop receiving emails from it, send an email to stell...@googlegroups.com.

Bartek Nowotarski

unread,
May 6, 2020, 9:07:23 AM5/6/20
to Jonathan Jove, Stellar Developers
A few comments (sorry if any of them was already mentioned):
  • Not sure how deeply we want to explain changes in existing ops behaviour but it looks like with a sponsored account you can send CreateAccountOp with startingBalance = 0 and right now this results in CREATE_ACCOUNT_MALFORMED.
  • Similar and maybe connected to what Nicolas already mentioned regarding: "base reserve calculation". All ops moving lumens/assets from one account to another use addBalance function in stellar-core that needs to be updated with sponsored entries. Without it, as explained in the previous bullet, a sponsored account with startingBalance = 0.001 wouldn't be able to send it anywhere. In "Available Balance and Limit of Native Asset" you explain how it changes in accounts sponsoring entries but we probably need a similar thing for accounts being sponsored.
  • Last thing connected to accounts: because SponsorshipEntry is saved in the ledger everyone knows account IDs of sponsored accounts and can create them before a sponsor actually wants them to be created. This may lead to CREATE_ACCOUNT_ALREADY_EXIST errors or having to increase the balance (because everyone can create it with startingBalance = 0). A solution could be to allow a specific account to send CreateAccountOp creating a sponsored account.
  • I imagine that some exchanges would like to sponsor creating a few offers for their users but without a market restriction (so a few trust lines too). I can see your answer about maximum cardinality matching. I have problems understanding why it matters. Can you elaborate?
Bartek

--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/CAG4FKhycnUsUHcyjUg-5N4tDhvRMGeXWTgQAyxENjppruTnRJQ%40mail.gmail.com.

Jonathan Jove

unread,
May 19, 2020, 5:33:08 PM5/19/20
to Bartek Nowotarski, Stellar Developers

Hey everyone,


Based on all of the excellent feedback here and some other conversations, I have prepared a new proposal for a similar mechanism https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md. The new proposal avoids many of the issues in the original proposal by removing the SponsorshipEntry. Instead, sponsorship decisions in the new proposal are made off-chain via a “sandwich approach” analogous to CAP-18. 


Again, I would appreciate any and all feedback while we try to make progress on this proposal.


Thanks,

Jon

Nicolas Barry

unread,
May 19, 2020, 9:14:41 PM5/19/20
to Jonathan Jove, Bartek Nowotarski, Stellar Developers
Hey Jon, this looks great.

Here are a few comments for you.

ClaimableBalanceEntry

Provide an actual diff here as it's supposed to tell us what changes compared to CAP0023

 

 

EphemeralSponsorshipEntry, GeneralizedLedgerEntry and GeneralizedLedgerKey

Those are not part of the protocol for now. I would move this to an appendix as it seems to be an implementation detail of how "ephemeral" entries could be added and integrated.

 

Related to this, LedgerEntryType should probably not be changed to include EPHEMERAL types as well. On the implementation side, I'd recommend not using small numbers like "5" in the protocol spec to avoid creating gaps in LedgerEntry types (ie, use negative numbers for ephemeral types seems like an option and just document that "negative numbers are reserved" in the protocol spec).

 

That said, on the implementation front there are probably better options: we can create a type that wraps LedgerKey / LedgerEntry without having to extend enums which doesn’t require any coordination between protocol spec and implementation.

 

ManageSponsorshipOp

 

I'd recommend making the management of ephemeral entries stricter:

  • Adding a parameter (bool) to the operation to explicitly state if it's a create or delete
  • replacing the sponsor of an ephemeral entry should error out (ie, there should be an explicit removal of old, followed by adding new)

 

Reason for this is that it makes it easier to validate and co-sign; and it leaves the door open to more complex sponsorships semantics in the future if we ever need them (like if we need to allow multiple sponsorships we'd want to create multiple ephemeral entries for the same thing at the same time).

 

Also, porting from the previous proposal: we should probably not allow accounts to sponsor themselves. It's at best a mistake, and probably makes implementations more complex.

 

RevokeSponsorshipOp


I see that you're checking `if AvailableBalance(source, NATIVE) < requiredReserve` - but it's probably better to use the generic code (that you're probably going to use in other places) to see if we should sponsor that entry (if there is an ephemeral key in place that makes it happen). Of course in this case we would not update the number of sponsored entries.

 

Other Operations

Should it be possible to sponsor an entry after it's created? If so, we probably need a new `SponsorEntryOp` that takes a ledger key as parameter.




Jonathan Jove

unread,
May 20, 2020, 5:12:36 PM5/20/20
to Nicolas Barry, Bartek Nowotarski, Stellar Developers
Hey Nicolas,

Thank you for the quick feedback!

ClaimableBalanceEntry

Sure, I’ll add a diff.


EphemeralSponsorshipEntry, GeneralizedLedgerEntry and GeneralizedLedgerKey

Good idea to move this all to an appendix. We should discuss more about the exact implementation details on GitHub, since it will depend a lot on specific factors in stellar-core and specifically in LedgerTxn.


ManageSponsorshipOp

Making replace return an error is fine by me, I had actually considered that while I was drafting it. I agree it opens the window for more complex semantics in the future. If we go this route, then I think we should make it extremely strict. Deleting a sponsorship that doesn’t exist should also be a failure, in my opinion.


As for changing the parameterization, I think this is purely a matter of aesthetics and user experience. I’m not that picky about it. One design that I had considered was to use “AccountID* sponsoredID”. This is similar to your boolean to determine whether it is create or delete. In this context the behavior would be “delete current sponsor” if sponsoredID is not set, “create the sponsor” if sponsoredID is set (and it is not currently sponsored), and invalid if sponsoredID is set to the source account. Let me know if you are happy with this.


RevokeSponsorshipOp

This is an excellent point. I didn’t consider the following situation: some account is sponsoring future reserves for B when a sponsorship is revoked. Good catch.


Other Operations

I don’t hold a strong opinion about whether or not this should be possible. I can’t currently see a good use for it, but of course that doesn’t mean that there isn’t one. We should probably wait to see if people want this feature, and if so plan it for a future protocol upgrade. In order to maintain the strong backwards compatibility guarantee mentioned in “Why Should Sponsorship be Ephemeral?” we would need to make it contingent on the ManageSponsorshipOp sandwich, so that both sponsoring and sponsored accounts must sign.


Best,

Jon

Leigh McCulloch

unread,
May 21, 2020, 1:45:46 PM5/21/20
to Stellar Developers
Hi Jon,

Some questions and thoughts:

1 –
An EphemeralSponsorshipEntry can be created, modified or deleted only by the ManageSponsorshipOp. Sponsored reserves can be reclaimed under some conditions by RevokeSponsorshipOp. 

I don't have an issue with how this works, but it is a little confusing as a developer using this to think of deleting sponsorship and revoking sponsorship as two different things. 

2 –
struct AccountEntryExtensionV2
{
    uint32 numSponsored;
    SignerSponsorship signerSponsorships<20>;
};

Since the account entry only has the number sponsored entries on it, does that mean a user can remove an entry and then add a new entry that uses a sponsorship? I assume no, that doesn't seem to be the intention. If a sponsored entry is removed from a users account, do they just have extra sponsored entries that are unused until the sponsor revokes them reclaiming them?

3 –
This applies even in the case of sponsorship for CreateAccountOp, whereas CreateAccountOp can usually be used without a signature from the created account.

I really like that it isn't possible for one account to fiddle with another account without that other account also signing the transaction. I think in practice this will make an application sponsoring accounts to require more back and forth. That won't be impossible, but especially in the case of a create account operation we need to lock up a channel account's next sequence number and the back and forth with a client will cause that to be locked up for much longer. I want to think about this a bit more.

4 –
operations[2]:
    sourceAccount: A
    body:
        type: MANAGE_SPONSORSHIP
        accountID: A

The manage sponsorship operation being used twice does not make the set of operations very descriptive, and for a transaction that contains multiple it's difficult to see where the sandwich begins and ends. I also think it's unintuitive that the source account of the last one is the sponsored account, but I understand that is necessary to require their signature. Is there a reason we couldn't use two separate operation types to more clearly describe what is happening and to make it clear why the sponsored account is the source account of the last? I'm thinking something like the below which would be described in simple english as the sponsor sets sponsorship and the sponsored account confirms all sponsors at the end. This would work for changing the sponsor during the transaction too:

sourceAccount: C
fee: <FEE>
seqNum: <SEQ_NUM>
timeBounds: <TIME_BOUNDS>
memo:
    type: MEMO_NONE
operations[0]:
    sourceAccount: S
    body:
        type: SET_SPONSORSHIP
        accountID: A
operations[1]:
    sourceAccount: C
    body:
        type: CREATE_ACCOUNT
        destination: A
        startingBalance: <STARTING_BALANCE>
operations[2]:
    sourceAccount: A
    body:
        type: CONFIRM_SPONSORSHIP
        accountID: A

Leigh

To unsubscribe from this group and stop receiving emails from it, send an email to stell...@googlegroups.com.

Tom Quisel

unread,
May 21, 2020, 3:05:37 PM5/21/20
to Leigh McCulloch, Stellar Developers
I'm very excited about the direction of this CAP!

The semantics all seem reasonable to me. Leigh brings up a good point about sequence number management for channel accounts, but I don't easily see a way around that.

As far as how it's implemented, I'm curious why the CAP adds the complexity of ephemeral and generalized ledger entries. I think this is similar to Nico's point. My first reaction is that there's state to be maintained inside Stellar core while processing a transaction that keeps track of sponsorships, but I don't see why that state would be thought of in the same way or even a similar way as ledger entries. It seems like the two concerns could be completely separated, and you could avoid complicating each with details of the other.

And finally is just a point that others have surfaced as well: I think it'd be more intuitive to replace MANAGE_SPONSORSHIP with a pair of  START_SPONSORING and STOP_SPONSORING operations. That makes it easier to understand when viewing & signing a transaction in a wallet, for example, without needing to examine the context (was there a MANAGE_SPONSORSHIP earlier?) to understand what a particular operation is doing.

Cheers,
Tom

To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/940b0726-79e2-49ec-9d9e-d68df6c2437f%40googlegroups.com.

Jonathan Jove

unread,
Jun 3, 2020, 1:47:55 PM6/3/20
to Tom Quisel, Leigh McCulloch, Stellar Developers
I've updated the proposal to reflect the helpful feedback. The proposal can be found at https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md.

Leigh McCulloch

unread,
Jun 5, 2020, 6:25:18 PM6/5/20
to Stellar Developers
I agree with Tom above that I'm very excited with this CAP and I think the position around sequence numbers that is being added makes sense.

It's occurred to me that it's not straightforward how businesses or individuals will balance accounts, for accounting purposes, that own reserve funds and or who have created claimable balances. Today all funds on the network, excluding the fee pool, live in accounts. In the case of sponsorship or claimable balances there will be funds that are still someones property but they will live outside of any account. This seems unavoidable with claimable balances but maybe less so with reserves. Balancing accounts on the Stellar network is already not trivial and this will make it significantly more difficult so I think it is worth considering.

We should probably consider how sponsor reserve funds and funds in claimable balances will be reflected in Horizon's API. I don't think that we have discussed whether that poses any significant challenge we need to consider. And if it does pose a significant challenge whether the trade off to keep reserve funds inside the sponsor account, which makes the account not-deletable, is a worthy tradeoff to make.

Eric, do you already have any thoughts about this or any concerns?

Leigh

--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stell...@googlegroups.com.

Eric Saunders

unread,
Jun 5, 2020, 7:30:23 PM6/5/20
to Leigh McCulloch, Stellar Developers
I've been waiting for a little more stability in the CAP implementation, so I haven't thought about this in detail yet. But my rough thoughts are

* current claimable balances would be accessible through a new horizon endpoint. Rather like the accounts endpoint, it would be filterable by at a minimum the createdBy and claimants. That handles use cases like "what are the set of claimable balances I have created?" and "what are the set of claimable balances I have access to?"
* on sponsored reserves: I'm imaging something similar, you can list the reserves you are currently sponsoring. I think this could turn out to be complex to implement if the sponsored reserves live outside of the account (which I think is the case with the current XDR in CAP 33?).

Where it gets murkier is effects and historical information. We need to think through the use cases around tracking over time. Ideally one could use effects to get a line by line understanding of how overall funds are tied up or freed. It's not clear to me yet how much work this would be. Historical information may be hard/impossible at the moment, although we have a proposal to change effects to mirror txmeta effects more directly, which could help a lot.

I propose we create a small working group who can meet next week to lay out the use cases explicitly, and consider the features needed to support them. Some of them may be critical, others may be ok to implement later. If there are any showstoppers we should find and feed them back ASAP.

Eric

To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/5538cf5c-ed7a-4622-bb36-770df7cdb867o%40googlegroups.com.

Leigh McCulloch

unread,
Jun 8, 2020, 10:47:41 PM6/8/20
to Stellar Developers
The changes Jon just pushed that keep the reserve lumens in the sponsors account address the concerns I had around keeping funds in accounts (and claimable balances) and that should making it easier to balance accounts.

Leigh

Leigh McCulloch

unread,
Jun 23, 2020, 1:20:38 PM6/23/20
to Stellar Developers
Hi Jon,

It has occurred to me that it will be difficult to use CAP-33 in wallets that also use SEP-30 because the transaction that adds a signer must also contain operations with the sponsor as a source account.

One of the rules that a SEP-30 implementation follows is that a server only signs transactions where the source account of the transaction and any operation is the registered account. This is intentionally limiting to ensure that a SEP-30 server that uses a shared key doesn't unwittingly sign a transaction for a different registered account on the server than the account that has been authenticated. If a wallet is using SEP-30 and CAP-33 they'll need to be able to use the set options operation sandwiched by sponsorship operations and have the SEP-30 servers sign that transaction but a SEP-30 server will reject that transaction.

A simple approach to making SEP-30 compatible with CAP-33 would be to add a requirement to SEP-30 that any implementation must use a unique signing key for each registered account and then the rules around what transactions it signs with those keys could be relaxed to sign any transaction regardless of contents. Our own implementation of SEP-30 is in the process of implementing that capability, but I think it would be unfortunate if SEP-30 required unique signing keys as it significantly increases the complexity of implementing the SEP and without that change the SEP is rather simple.

Another approach to making SEP-30 compatible with CAP-33 would be to allow sponsorship operations to have other source accounts, on the assumption that the sponsor account is not a registered account of the SEP-30 servers. This is an unintuitive feature though that if a sponsor was not aware they could make their account vulnerable to others utilizing their sponsorship capabilities without their authorization.

I don't yet have a proposal for a solution to this problem that I'm happy with, but I want to highlight the issue early because this would prevent me from using sponsorship within Vibrant. Previous proposals where sponsorship and the consumption of that sponsorship were disconnected did not have this issue.

Leigh

Leigh McCulloch

unread,
Jun 23, 2020, 1:23:40 PM6/23/20
to Stellar Developers
Another approach to making SEP-30 compatible with CAP-33 would be to allow sponsorship operations to have other source accounts, on the assumption that the sponsor account is not a registered account of the SEP-30 servers. This is an unintuitive feature though that if a sponsor was not aware they could make their account vulnerable to others utilizing their sponsorship capabilities without their authorization.

Actually this idea I suggested has a larger issue in that any registered account could be used to sponsor any other registered account, so this idea is definitely a no-go.

Leigh McCulloch

unread,
Jun 29, 2020, 7:10:13 PM6/29/20
to Stellar Developers
Jon and I have been talking about the names of the ops again and think we should change them.

They are currently:
SPONSOR_FUTURE_RESERVES
CONFIRM_AND_CLEAR_SPONSOR
UPDATE_SPONSORSHIP

We think we should rename the first two to:
BEGIN_SPONSORING_FUTURE_RESERVES
END_SPONSORING_FUTURE_RESERVES

When looking at the names of the Horizon effects that might be given names like sponsorship_created and sponsorship_deleted it could be confusing that the clear operation doesn't result in sponsorship deleted. A problem with the existing names is it is visually unclear that there is a sandwich. If we make the sandwich clearer by using symmetrical op names it's more intuitive that the sandwich ops together create the sponsorship and will result in that effect. A concern with this rename is it is no longer clear that a different account signs for the second op. I think this is a small detail and a tradeoff – there's more value in communicating the sandwich clearly.

We think we should rename the last to:
REVOKE_SPONSORSHIP

The UPDATE_SPONSORSHIP op always has the source account set to the account that will no longer be paying the reserve. It is either the sponsor who is giving up their sponsorship, or the owner who will no longer be paying the reserve themselves. While it's true it updates the sponsor, I think it's simpler to explain if we make it clearer that it is the op that is the current sponsor acknowledging they are being removed as a sponsor. The other ops set a new sponsor.

Thanks,
Leigh
Message has been deleted
Message has been deleted
Message has been deleted

Jonathan Jove

unread,
Jul 9, 2020, 3:44:37 PM7/9/20
to Stellar Developers
I want to continue the conversation from the Open Protocol Meeting on 7/9 regarding the appropriate predicate structure. Some options and preliminary thoughts on them:

- Unlimited recursion in XDR but limited to a certain depth in validation logic. Advantage: it is the most flexible, and easy to implement. Disadvantage: it requires special validation logic, malicious infinite recursion, and can be the hardest to reason about
- Limited recursion in XDR. Advantage: it is the most flexible, no malicious infinite recursion, and doesn't require special validation logic. Disadvantage: it can be hard to work with (many different similar structures), and can be the hardest to reason about
- XDR-opaque. Advantage: it is the most flexible, and no malicious infinite recursion. Disadvantage: less performant (requires additional marshalling), requires special validation logic, and can be the hardest to reason about
- Disjunctive Normal Form. Advantage: it is the simplest to reason about, easy to implement, and no malicious infinite recursion. Disadvantage: less flexible, less space efficient (accountIDs are large relative to predicates)

There are probably a few other possibilities in this space too. For example, Bartek proposed an RPN approach which was conceptually interesting to me but Graydon opposed.

The other open question from the meeting is whether we should support predicates like `_BEFORE` and `_AFTER` or have a generic `_NOT`. `_NOT` is elegant in the sense that it doesn't require a lot of duplication, but some predicates such as revealing a hash-preimage don't make a ton of sense with `_NOT`. Thoughts?

Thanks,
Jon

On Mon, Jul 6, 2020 at 12:52 PM 'Carson Flint' via Stellar Developers <stell...@googlegroups.com> wrote:
After submitting my previous comment, I realize the base reserve was to keep the ledger smaller and not prevent network transaction spam.
I still think it you should reduce the financial load of sponsoring many accounts.
Like if the source funding is large enough the base reserve amount is reduced by a percentage.

Sincerely,
Carson Flint

On Wednesday, July 1, 2020 at 7:18:17 AM UTC-4 Carson Flint wrote:
Again, correct me if I am wrong, but I believe the whole point of the minimum reserve is to prevent network spam.
If so, would it make sense to rate-limit sponsored accounts?

For example, you sponsor an account with an amount less than the minimum reserve and thus the account becomes rate limited accordingly.
Sponsor account with 1 stroop = account can make one operation every 48 hours
Sponsor account with 0.2 xlm = account can make one operation every 30 minutes
etc

Sincerely,
Carson Flint
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/da3b644f-65e5-4e4c-8800-3dee477b378bn%40googlegroups.com.

Nicolas Barry

unread,
Jul 9, 2020, 7:15:29 PM7/9/20
to Jonathan Jove, Stellar Developers
On Thu, Jul 9, 2020 at 12:44 PM 'Jonathan Jove' via Stellar Developers <stell...@googlegroups.com> wrote:
I want to continue the conversation from the Open Protocol Meeting on 7/9 regarding the appropriate predicate structure. Some options and preliminary thoughts on them:

- Unlimited recursion in XDR but limited to a certain depth in validation logic. Advantage: it is the most flexible, and easy to implement. Disadvantage: it requires special validation logic, malicious infinite recursion, and can be the hardest to reason about
- Limited recursion in XDR. Advantage: it is the most flexible, no malicious infinite recursion, and doesn't require special validation logic. Disadvantage: it can be hard to work with (many different similar structures), and can be the hardest to reason about
- XDR-opaque. Advantage: it is the most flexible, and no malicious infinite recursion. Disadvantage: less performant (requires additional marshalling), requires special validation logic, and can be the hardest to reason about
- Disjunctive Normal Form. Advantage: it is the simplest to reason about, easy to implement, and no malicious infinite recursion. Disadvantage: less flexible, less space efficient (accountIDs are large relative to predicates)

There are probably a few other possibilities in this space too. For example, Bartek proposed an RPN approach which was conceptually interesting to me but Graydon opposed.

I think that from what we discussed this morning, the starting point for the discussion should be to use approach 1, which happens to be the one in the existing proposal:
* something we didn't mention this morning is that predicates are checked at submission time, so from a usability point of view it's not that different from an SDK level check
* I'd argue that the DNF form for complex expressions (if we ever decide to have something a bit more complicated in the future) is actually pretty hard to read for a human

Anyways, in order to avoid bikeshedding, I think that switching to some other implementation would have to be provably vastly superior.

Based on this last point, I would not be opposed to reduce the limit to the smallest limit needed for known use cases at this time if it translates to an actual simplification in SDKs - which may not hold if the limit is 2 more (we already know that this would not simplify core's implementation).


The other open question from the meeting is whether we should support predicates like `_BEFORE` and `_AFTER` or have a generic `_NOT`. `_NOT` is elegant in the sense that it doesn't require a lot of duplication, but some predicates such as revealing a hash-preimage don't make a ton of sense with `_NOT`. Thoughts?

Regardless of how we decide to represent expressions, it's entirely possible to write predicates that always evaluate to TRUE or FALSE, so I am not sure hash-preimage would be that special in that context (ie, just don't do that).

Having a NOT operator on the other hand allows to express arbitrary expressions regardless of which new conditions get introduced and without having to introduce the explicit negated version.
Also, a NOT operator allows to negate an entire sub-expression, which can help a lot in ease of authoring correct smart contracts as by design branches are mutually exclusive.

Reply all
Reply to author
Forward
0 new messages