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

fix: enforce minimum for pegins #5808

Closed
wants to merge 1 commit into from

Conversation

joschisan
Copy link
Contributor

No description provided.

@joschisan joschisan requested review from a team as code owners August 6, 2024 16:48
@joschisan joschisan marked this pull request as draft August 6, 2024 16:55
Comment on lines 316 to 317
#[error("The pegin amount is not economically viable for the federation")]
PegInUnderMinimum,
Copy link
Contributor Author

@joschisan joschisan Aug 6, 2024

Choose a reason for hiding this comment

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

Can we add an error variant here here with even though we have no encodable default variant yet? Do we need to back port this to 0.4 first?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I believe we'd break compatibility with existing 0.3.* and 0.4.0 clients even if we backport. It's not semantically correct, but perhaps we can reuse UnknownInputVariant to maintain compatibility.

@joschisan joschisan force-pushed the wallet_pegin branch 16 times, most recently from b5c45fb to 5f4e95e Compare August 7, 2024 07:21
@@ -1002,6 1009,19 @@ mod fedimint_migration_tests {
);
info!("Validated FeeRateVote");
}
DbKeyPrefix::PegInMinimumVote => {
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be useful to add a test to verify peg-ins below the threshold are rejected

@@ -398,6 406,8 @@ impl ServerModule for Wallet {

items.push(WalletConsensusItem::Feerate(fee_rate_proposal));

items.push(WalletConsensusItem::PegInMinimum(Amount::from_sats(10_000)));
Copy link
Member

Choose a reason for hiding this comment

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

Instead of hardcoding, should we consider allowing peers to define the threshold with an env var (or other approach)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wanted to do this in a follow up. This is just a placeholder value for now - 10_000 is the highest we can set it to before our test would fail, however, not a sufficient minimum which I assume would be at leats 1_000_000 sats.

@@ -398,6 406,8 @@ impl ServerModule for Wallet {

items.push(WalletConsensusItem::Feerate(fee_rate_proposal));

items.push(WalletConsensusItem::PegInMinimum(Amount::from_sats(10_000)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this detect if the current vote is already equal to this value and submit citem only if they are not equal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would be a unnecessary optimisation as redundant votes are filtered out after the ordering. This would need to be done though if we ever are able to stop aleph bft when no new items need to be ordered.

Copy link
Contributor

@dpc dpc left a comment

Choose a reason for hiding this comment

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

BTW. I'm of the opinion that I'd rather see a per peg-out fee that is substracted from peg-in amount, and used as a subsidy during peg-out. Peg-ins smaller than the fee would effectively be worth zero, and such outputs could be ignored for the purpose of peg-outs, leading to a similar but more general and smoother approach.

@joschisan
Copy link
Contributor Author

joschisan commented Aug 8, 2024

@dpc so if I understand correctly we would, for every pegin, calculate the necessary fee x to add this new UTXO to an transaction given the current fee rate. Then we would subtract x from the value of the UTXO, fail if x is larger then the UTXO's value, and write it in the db with the pegged in spendable UTXO. When this spendable UTXO is used for a peg out we can subtract the pegin fee from the total value of the output that's needs to be funded via ecash. Now we would calculate again the current fee x and and if x is larger then the change value we would add x to the fedimint tx output value as well as add the change output.

Assuming a constant fee level this would correctly attribute fees to the user as the issued value of ecash issued in a begin is exactly its economical value.

However, we might incur substantial loss of economic value when fee levels rise here. User might pegin a utxo of value 5x (again where x is the fee) and only a 5x increase in feerate would decrease that UTXOs actually economic value to zero. You could, of course, use a multiplier for pegin fees, to account for possible fee spikes, however then in the case of more or less constant fees users pegging in would overpay fees.

Actually, this seems to me that this would create an economic incentive for users to consolidate the federations UTXO's with circular deposits. So, assuming a sufficient multiplier, we should see users consolidating UTXOs into more or less a single UTXO right after they were pegged in.

Note here that depending on the UTXO selection algorithm consolidation requires more or less capital to select the UTXOs to consolidate. If we were to allow the user to select UTXOs it would not though.

@dpc
Copy link
Contributor

dpc commented Aug 8, 2024

@joschisan

given the current fee rate.

I think the peg-in fee substracted from the peg-in should be based on some long-term expected feerate margin of error, but yes.

We should discuss over voice chat sometime, as there's a lot of possibilities and nuances.

@elsirion
Copy link
Contributor

elsirion commented Aug 9, 2024

However, we might incur substantial loss of economic value when fee levels rise here.

Isn't that also the case for minimum deposit amounts? I guess there you can be more conservative and disallow deposits that are smaller than the highest fee we'd want to charge.

Personally I'm more of a fan of the fee model since no BTC are getting lost (in the min deposit case we'd either need refund logic or we'd be burning BTC). To further disincentivize small deposits we could have an extra penalty fee for small deposits though, kind of a regressive fee schedule.

@joschisan
Copy link
Contributor Author

joschisan commented Aug 9, 2024

Isn't that also the case for minimum deposit amounts?

Let's say the fee to add a UTXO to a transaction at the moment is x, someone pegs in a UTXO of value 5x, which has economic value of 4x, and therefore we charge a fee of x to for pegging in the UTXO, hence we issue ecash for the value 4x. Then the ferrate doubles. The economic value of that UTXO is now 3x = 5x - 2x but we issued ecash for 4x - a loss of 25%.

Let's say we try to mitigate this by using a contanst multiplier for pegin fees. As soon as the feerate increases more then this multiplier between pegin and peg out. Lets repeat the calculation above for an example: UTXO of 5x, we subtract 2x in fees an issue ecash with value of 3x. Then the ferrate triples. Now the UTXOs economic value is 2x = 5x - 3x but we issued ecash for 3x - a loss of 33%.

Also note how the multiplier creates economic incentive for consolidation via circular pegins as described above.

Lets say now we limit the pegin amount to 100x or more. Now a user pegs in the minimum of 100x and we issue ecash for 100x. Economic value of that UTXO is 99x. Now the ferrate doubles and the economic value of that UTXO is 98x - a loss of 1%.

@joschisan
Copy link
Contributor Author

To further disincentivize small deposits we could have an extra penalty fee for small deposits though, kind of a regressive fee schedule.

The problem I see here is that you are penalising the proper use to disincentive misuse that occurs cost on other users. There's an inherent tradeoff here. Furthermore, we need to consider the case that someone might act against their economic incentives maliciously, precisely to incur costs to other users.

This seems like a fundamental tradeoff in the fee model which is why I settled on pegin minimums in the first place even though some more flexible fee solution were my initial approach as well.

@dpc
Copy link
Contributor

dpc commented Aug 9, 2024

Isn't that also the case for minimum deposit amounts?

Let's say the fee to add a UTXO to a transaction at the moment is x, someone pegs in a UTXO of value 5x, which has economic value of 4x, and therefore we charge a fee of x to for pegging in the UTXO, hence we issue ecash for the value 4x. Then the ferrate doubles. The economic value of that UTXO is now 3x = 5x - 2x but we issued ecash for 4x - a loss of 25%.

@joschisan I don't get this answer. It doesn't explain how is anything better with minimum deposits.

There is no magic solution. Hypothetically if the feerate goes to 1000sat/vB, tons of stuff on Bitcoin becomes economically unspendable. But what we can safely assume is that they they become temporarily unspendable. History shows that fee spikes are temporary.

The point of substracing a fee is to move the cost of spending an UTXO from the UTXO spender to the one that is responsible for creating the UTXO (pegin). It doesn't need to be perfect, but IMO is just reasonable and fair.

Yes, it should incentivize consolidating. Moroever - I think wallet module should just not do any fancy coin selection, and just always consolidate as much as possible (maybe with some cap to avoid humungus UTXOs) trying to get as close as possible to a single UTXO that can always be CPFPed.

If the Federation collects pegin fee for each UTXO at let's say 30sats/vB * onchain_input_size, then during times of 30sats/vB or less on chain fees, all pegouts are fully refunded for UTXO spend cost. During times of fee rates being higher, the user needs to pay up and cover the difference or just wait. IMO that's the best we can do given that we can't control on chain fees, and we don't want guardians to loose money.

The exact pegin feerate could be adjustable via consensus voting (like most stuff), so guardians that predict long term higher fees can adjust it over time.

@elsirion

@elsirion
Copy link
Contributor

To further disincentivize small deposits we could have an extra penalty fee for small deposits though, kind of a regressive fee schedule.

The problem I see here is that you are penalising the proper use to disincentive misuse that occurs cost on other users. There's an inherent tradeoff here. Furthermore, we need to consider the case that someone might act against their economic incentives maliciously, precisely to incur costs to other users.

You don't have to penalize proper use. If you have a minimum deposit people will burn their BTC, so you might as well just accept the peg-ins with a 100% fee going to fee subsidies. When fees are low (subsidy reserve of UTXO < spend cost) these UTXOs can be used for peg-outs and any remaining subsidy reserve either goes to the guardians or gets carried over to the change UTXO. That way we avoid burning BTC while at the same time heavily disincentivizing small deposits.

@joschisan
Copy link
Contributor Author

joschisan commented Aug 14, 2024

I don't get this answer. It doesn't explain how is anything better with minimum deposits.

@dpc Let me reframe this then. With a minimum deposit and minimum change size of m, a peg out transaction for a value of t can be constructed with a maximum t // m 2 inputs (the division rounds down here) - irrespective of the coin selection actually. Now given the the fee of x to add a utxo to a transaction, the fee to add all selected inputs to the peg out transaction is bound by x * (t // m 2) hence the relative fee with respect to t is bound by

(x * (t // m 2) ) / t <= x / m (2 * x) / t

Hence for t >= m you have a maximum relative pegout fee of 3 * (x / m) while the relative pegout fee is asymptotically approaching x / m from above for increasing t.

To give two concrete examples for t = 2m we can bound the relative pegout fee by 2 * x / m while for t = 10 * m the bound is (6/5) * (x/m)

Please note how the bounds above depend only on the ferrate through x and the choice of m but not on behaviour of other users. It seems to me that without minimum pegin amounts it is not possible to derive such a bound on relative pegout fees... or can you? As I said, I thought about a similar approach to your fee subsides first but switched to minimum begins as I was unable to construct a bound on relative peg out fees.

In my opinion, disincentivizing behaviour that incurs a cost on other users is not sufficient, we need to provide a bound. Also users might act against their own economic interests by pegging in small amounts as we have already seen with the UTXO sets of public feds.

@dpc
Copy link
Contributor

dpc commented Aug 21, 2024

@joschisan Oh. That is some math I would have to sit and think about. If I don't misunderstand completely, you try to make some bounds and estimates on amount of UTXO needed to make a peg-out, given certain properties of UTXOs from peg-ins, etc. which is probably smart and all, but fundamentally seems to me like the wrong way to think about it.

My take here is that there's a simpler way: every peg-in UTXO has a known vB cost when used in peg-out, known upfront for a given Federation. As long as the pegged-in user bares the cost (a peg-in fee) of each peg-in UTOX, it doesn't matter if a peg-out transaction requires 1 or 100 UTXOs - each of them would carry a subsidy to typically pay for its spend during peg-out (or if too small would be entirely ignored).

Obviously an actual fee is vB-size * feerate, but any solution is affected by a feerate uncertainty, and I know it's actually fundamentally unsolvable problem, that needs to depend on some conservative long-term fee estimate no matter what.

The core observation here is that every created UTXO implies one and only one (eventual) cost of spending it. No amount of math or UTXO selection can change that fact. The feerate can be different at different times of spending it, but this is always an educated gamble trading over-charging vs under-charging.

If the peg-in fee (=peg-out subsidy) is calculated with vB-size * expected-long-term-feerate where the expected-long-term-feerate is reasonable, this solution works as long as fee-rates during peg-outs are not much higher than that conservative value. To prevent accumulation of UTXOs and problems with spending them, I also think that "a rolling change" (spending all/most available and economical UTXO during every peg-in the best approach) is the best approach.

@bradleystachurski
Copy link
Member

@dpc for context, we discussed this PR during last week's deep dive.

https://bitcointv.com/w/reHSR15G1huyXGvu6i4CFG

Obviously an actual fee is vB-size * feerate, but any solution is affected by a feerate uncertainty, and I know it's actually fundamentally unsolvable problem, that needs to depend on some conservative long-term fee estimate no matter what.

I agree the fundamental issue is feerate uncertainty. Thinking from a financial hedging perspective, the problem we face is similar in nature to an airline selling tickets for a future flight without knowing the future cost of jet fuel. To minimize uncertainty, the airline can hedge their risk of increased costs by going long jet fuel derivatives. Since federations don't have the luxury of hedging costs by going long feerate derivatives (yet), the next best approach is to minimize the time between when a peg-in fee is collected and when the peg-in UTXO is spent in a transaction.

During the deep dive, @m1sterc001guy proposed an interesting idea: once the user submits a peg-in proof, immediately construct a transaction that consolidates the peg-in UTXO passing the fee to the user (see ~23:50 in the deep dive video). That approach seems to solve the problem of minimizing the time between collecting the peg-in fee and spending the peg-in UTXO.

When we implement a fix for submitting peg-in proofs that exceed ALEPH_BFT_UNIT_BYTE_LIMIT (#5585), we can also trigger constructing a consolidation transaction once a threshold of peers verify the proof. The client will wait until it observes a threshold of peers that verified the proof and signed a consolidation transaction, subtracting the fee used in the consolidation transaction when they call submit_transaction. If the peg-in UTXO isn't sufficient to pay for a consolidation transaction, the user won't be able to claim any ecash.

I still need to chew on the implications of doing this in the existing wallet module vs only introducing this behavior in a wallet v2 (all existing federations using old wallet, all new federations using v2).

Please call out any issues you see with this approach. I'm also happy to hop on a call to discuss.

@dpc
Copy link
Contributor

dpc commented Aug 21, 2024

During the deep dive, @m1sterc001guy proposed an interesting idea: once the user submits a peg-in proof, immediately construct a transaction that consolidates the peg-in UTXO passing the fee to the user

Please call out any issues you see with this approach.

That still leaves you with an UTXO (one, but still) that is a subject to feerate uncertainty.

Also, let's compare the cost. If we have 3 peg-ins first and then 3 peg-outs later, assuming 2 first peg-outs create a change UTXO, and last one spends the last UTXO whole, eagerly consolidating after peg-ins goes though 7 UTXOs total, while consolidating opportunistically on peg-outs only though 5.

Consolidate on peg-outs only:

peg-in 1 ; 1 unspent
peg-in 2 ; 2 unspent
peg-in 3 ; 3 unspent
peg-out 1; 3 unspent, 1 spent
peg-out 2; 3 unspent, 2 spent
peg-out 3; 0 unspent, 5 spent

Consolidate on peg-ins with extra tx:

peg-in 1 ; 1 unspent
peg-in 2 ; 1 unspent, 2 spent
peg-in 3 ; 1 unspent, 4 spent
peg-out 1; 1 unspent, 5 spent
peg-out 2; 1 unspent, 6 spent
peg-out 3; 0 unspent, 7 spent

There might be other scenarios that I'm too tired to go through, but generally I expect an extra consolidation to cost one extra UTXO.

Thinking from a financial hedging perspective, the problem we face is similar in nature to an airline selling tickets for a future flight without knowing the future cost of jet fuel.

That's a great way to think about it. Worth noting that sometimes one wins such a bet - the jet fuel gets cheaper, and airline pockets the difference. So the cost of uncertainty might be more than adequately reflected in a higher expected long-term feerate (fee subtracted from the peg-in amount). The federation might accumulate the surpluses from "winning bets" to afford subsidizing peg-outs during higher than expected fee rate, at least partially.

@bradleystachurski
Copy link
Member

@joschisan Is it worth closing this and focusing our efforts on wallet v2?

@joschisan joschisan closed this Oct 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants