-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Use ChargeFee in balances and contract modules.
#1815
Conversation
|
It looks like @shaopengw hasn't signed our Contributor License Agreement, yet.
You can read and sign our full Contributor License Agreement at the following URL: https://cla.parity.io Once you've signed, please reply to this thread with Many thanks, Parity Technologies CLA Bot |
1 similar comment
|
It looks like @shaopengw hasn't signed our Contributor License Agreement, yet.
You can read and sign our full Contributor License Agreement at the following URL: https://cla.parity.io Once you've signed, please reply to this thread with Many thanks, Parity Technologies CLA Bot |
|
[clabot:check] |
|
It looks like @shaopengw signed our Contributor License Agreement. 👍 Many thanks, Parity Technologies CLA Bot |
1 similar comment
|
It looks like @shaopengw signed our Contributor License Agreement. 👍 Many thanks, Parity Technologies CLA Bot |
|
Where's the code for the fees module? |
| if new_balance < Some(<balances::Module<T>>::existential_deposit()) { | ||
| return Err("not enough funds for transaction fee"); | ||
| } | ||
| <T as Trait>::ChargeFee::charge_fee(transactor, cost)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous version of code ensures that new_balance cannot be below existential deposit. In other words we can't kill an account buy purchasing gas. Looking at the current implementation this property won't hold anymore.
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.
@pepyakin Maybe in general ChargeFee (and TransferAsset in the implementation) trait are not supposed to kill transactor's account. Account got killed is more like an unexpected side effect while paying fees or doing transfer in other srml modules except balances. I could update the implementation of TransferAsset, to make the functions return Errs instead of killing accounts.
srml/contract/src/gas.rs
Outdated
| let refund = <T::Gas as As<T::Balance>>::as_(gas_meter.gas_left) * gas_meter.gas_price; | ||
| <balances::Module<T>>::set_free_balance(transactor, b + refund); | ||
| <balances::Module<T>>::increase_total_stake_by(refund); | ||
| <T as Trait>::ChargeFee::refund_fee(transactor, refund) |
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.
Because we ensure that buy_gas doesn't destroy account, the previous version can't fail. This one apparently can and if it fails for some reason, the whole transaction will be deemed as failed.
This be very misleading, given that the changes that were performed in that extrinsic are actually commited.
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.
If refund_fee always called after charge_fee and the amount of refund the less that charged, then it can't fail. refund_fee is designed to haveResult return type to make it more robust, as we can't guarantee how it is called.
Is it better to document this kind of expected way of usage, and get rid of Result return type? Or we ignore the return here as it can't fail in this case?
srml/contract/src/lib.rs
Outdated
| // NOTE: this should go after the commit to the storage, since the storage changes | ||
| // can alter the balance of the caller. | ||
| gas::refund_unused_gas::<T>(&origin, gas_meter); | ||
| gas::refund_unused_gas::<T>(&origin, gas_meter)?; |
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.
Note that here (and above) when we can't refund funds for some reason, we abort the execution of the extrinsic with an error, even though all changes are commited.
Moreover, we lose all calls recorded so far.
| type DetermineContractAddress: ContractAddressFor<CodeHash<Self>, Self::AccountId>; | ||
|
|
||
| /// Fee charge. | ||
| type ChargeFee: ChargeFee<Self::AccountId, Amount=Self::Balance>; |
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.
If we are ok with changes here regarding contracts, we need to document the requirements of ChargeFee implementation. I.e. refund_fee should fail and etc.
The fees module code has been merged in PR #1648 . |
|
I see now; missed that one. We'll let #1821 go in before this one. |
|
|
|
For enforcing ExistentialDeposit law, another possible solution is that, instead of kill an account instantly when balance below This could solve these situations:
|
|
@gavofyork what is the plan for this PR? I think this is going to be conflict with #1782. Do you intent to wait until #1782 is merged? |
|
Yes, but the good news is that it should be merged today or tomorrow so we can reevaluate how and if these changes make sense in the light of the api changes. |
|
Batching up deletions may make sense. But will need some thought as it introduces additional runtime costs that I’d rather not have. |
|
What additional runtime cost are you concerning about? |
Precisely my concern. |
srml/balances/src/lib.rs
Outdated
| Self::decrease_total_stake_by(fee); | ||
| T::ChargeFee::charge_fee(transactor, fee)?; | ||
|
|
||
| Self::set_free_balance(transactor, new_from_balance - fee); |
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.
Why is the fee subtracted again? What protects us from overflows?
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.
@tomusdrw If not subtracted, fee will actually be charged twice, since charge_fee has already removed fee from balance. Because fee was added to new_from_balance (by calculating liability), the subtraction here cannot fail.
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.
If I'm not mistaken it's the other way around:
let liability = match value.checked_add(&fee)...;
let new_from_balance = match from_balance.checked_sub(&liability)...;
T::ChargeFee::charge_fee(transactor, fee)?;
Self::set_free_balance(transactor, new_from_balance - fee);The fee is subtracted from from_balance and then again subtracted before setting the free_balance. So unless I'm missing something: new_from_balance = from_balance - value - fee - fee
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.
@tomusdrw You're right, my mistake. Will update.
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.
Updated. Also added two unit tests for balance transfer, to cover fee charge.
|
|
||
| Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); | ||
| assert_ok!(<Balances as TransferAsset<_>>::transfer(&1, &2, 1)); | ||
| assert_ok!(Balances::ensure_account_can_withdraw(&1, 1, WithdrawReason::Transfer, 9)); |
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.
These are not logically equivalent. Any such changes should be well-documented in the PR with why unit tests are being altered.
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.
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.
@shaopengw maybe should have assert_err!(<Balances as TransferAsset<_>>::transfer(&1, &2, 1));
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.
@xlc The assert_noop! below has covered the TransactionPayment withdraw should fail case, so it should be fine without adding assert err for transfer.
If these tests are mean to be unit tests, maybe other transfer calls in this test should also be changed to ensure_account_can_withdraw like this line, as transfer implies both WithdrawReason::TransactionPayment and WithdrawReason::Transfer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason of the change is old behavior is not correct.
balances transfer method not only withdraw for transfer reason, but also withdraw for tx fee reason
The old behaviour is correct. The only time transaction payment happens is prior to extrinsic execution in executive. Any further withdrawals are not "transaction payment" but high-level actions that are logically unrelated. The two should not be confused.
|
superceded in #2048 |
This is a follow-up change related to
feesmodule. (issue #1515 and PR #1648 )ChargeFeeis introduced intobalancesandcontractmodules, to handle fee charge logic:balances: transfer/creation fee.contract: gas fee buy/refund.