substrate icon indicating copy to clipboard operation
substrate copied to clipboard

Use can_hold instead of can_reserve in fn hold

Open KiChjang opened this issue 3 years ago • 17 comments

Per title, also tries to use safe math.

It seems to me that #8454 really intended the code to be calling InspectHold::can_hold rather than ReservableCurrency::can_reserve, but perhaps due to merge conflicts, the can_hold call has been clobbered by can_reserve.

Part of #8285 and #8453.

KiChjang avatar Aug 10 '22 15:08 KiChjang

Test?

shawntabrizi avatar Aug 10 '22 17:08 shawntabrizi

No test?

gavofyork avatar Aug 11 '22 11:08 gavofyork

Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 14 '22 05:09 stale[bot]

https://github.com/paritytech/substrate/pull/12004/commits/7a6db84850d68985e51e5fbd662f3bccaa208648 Looks good.

kianenigma avatar Oct 11 '22 07:10 kianenigma

Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 10 '22 09:11 stale[bot]

bot rebase

ruseinov avatar Nov 29 '22 15:11 ruseinov

Rebased

Would appreciate input in terms of logic soundness for can_reserve.

The important differences between the current code and the last review:

  1. keep_alive for can_hold has been reverted as we should never reap the account on hold.
  2. ensure_can_withdraw has been removed from can_reserve as it is a subset of the new logic.
  3. can_reserve now takes ExistentialDeposit into account to make sure there is no overdraft.
  4. reserve is now enforcing can_reserve.

What this basically means is that can_reserve basically replicates can_hold logic in terms of balance assumptions.

ruseinov avatar Nov 29 '22 17:11 ruseinov

The CI pipeline was cancelled due to failure one of the required jobs. Job name: test-linux-stable Logs: https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2110851

paritytech-cicd-pr avatar Dec 02 '22 13:12 paritytech-cicd-pr

A bit stuck on this one while fixing other pallets, duplicating my post in FRAME Element room for posterity:

Basically, after introducing the change to can_reserve and reserve we have to reserve here https://github.com/paritytech/substrate/blob/493b58bd4a475080d428ce47193ee9ea9757a808/frame/contracts/src/storage/meter.rs#L489 taking into account the ED. .and_then(|_| T::Currency::reserve(contract, *amount - ED)); . I'm not sure it's the correct behaviour. Any insight would be much appreciated. This also then breaks the refund logic.

ruseinov avatar Dec 03 '22 15:12 ruseinov

This breaks the current implementation of contracts pallet, currently blocked until @athei figures out how we want to go about those changes.

ruseinov avatar Dec 05 '22 11:12 ruseinov

Additional notes:

Modify pallet-balances to take care of the below:

having a non-zero reserved balance should imply a consumer ref.
anything which alters the reserved balance should inc consumer refs when it goes from is_zero() -> !is_zero() and dec the consumer ref when the opposite is
true.

But I'm not sure how this can solve the contracts issue. Because if we only transfer the ED - it will be unreserved and therefore there won't be a consumer ref.

ruseinov avatar Dec 06 '22 12:12 ruseinov

But I'm not sure how this can solve the contracts issue. Because if we only transfer the ED - it will be unreserved and therefore there won't be a consumer ref.

pallet-contracts transfers at least the ED on contract instantiation and then reserves it.

athei avatar Dec 06 '22 16:12 athei

But I'm not sure how this can solve the contracts issue. Because if we only transfer the ED - it will be unreserved and therefore there won't be a consumer ref.

pallet-contracts transfers at least the ED on contract instantiation and then reserves it.

Yeah, but we can't reserve the ED anymore due to the changes in can_reserve (which is called in reserve). Or rather we can, but we still need to have at least ED in free balance.

ruseinov avatar Dec 08 '22 10:12 ruseinov

I thought a while about this any my preferred solution would be that a contract can act as a sufficient for an account. It is a consumer because it depends on the existence of its account (and by extension its balance). But it is also a provider because it takes storage deposit which covers both the existential deposit and the data structures within pallet-contracts.

Up until now this was achieved by transferring this deposit to the contract and reserving it there. This does no longer work with this PR as the existential deposit must be free balance. This issue arises because pallet-contracts is a sufficient only indirectly (by enforcing a certain balance). I think that should change and pallet-contracts should call System::inc_sufficients directly when creating a contract. The workflow I imagine is as as follows:

  1. Contract is about to be instantiated
  2. System::inc_sufficients(contract)
  3. Currency::transfer(caller, contract, storage_deposit)
  4. Currency::reserve(contract, storage_deposit)

Step 4. is where it currently fails because we cannot reserve all the contract's balance as some of it needs to be free. We could work around this by partly leaving some balance free and creating a consumer to prevent the balance to be send away. However, I think that feels a bit weird when part of the deposit is free and some is reserved. Additionally, a storage migration is needed that changes all contract's accounts to have some free balance. This is why I suggest changing the can_reserve logic to the following which would allow the above workflow without a storage migration:

Allow reserving the ED (as it was the case before this PR) but remove the provider created by pallet-balances when doing so. If this would reap the account (because there is no other provider) we won`t allow the reserve because we never want the reserve to kill an account. Not sure if this is a sound solution. Need some more thinking and debate.

Another suggestion made by @gavofyork is to automatically add a consumer to an account whenever there is a non zero reserved balance. This would also work for contracts as we could send part of the initial balance as free balance as mentioned above. However, this also needs this big storage migration of all existing accounts with reserved balance.

athei avatar Dec 08 '22 16:12 athei

Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jan 14 '23 16:01 stale[bot]

This has been stale for a while as we did not reach consensus on how to proceed with this as far as I understand. This should fix it: https://github.com/paritytech/substrate/pull/12951

ruseinov avatar Jan 17 '23 16:01 ruseinov

Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Feb 17 '23 00:02 stale[bot]

Superseded by paritytech/substrate#12951.

gavofyork avatar Feb 22 '23 17:02 gavofyork