-
Notifications
You must be signed in to change notification settings - Fork 6
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
Feature: Dispenser Pallet #226
Conversation
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.
Most important thing to test out is that the extrinsic can actually be called for free, and we are not really testing that in the pallet test because all extrinsics are free.
After the pallet test actually tests that, I suggest testing it by running a local node with zombienet.
mod dispense { | ||
use super::*; | ||
#[test] | ||
fn user_can_dispense_for_free() { |
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 mock runtime doesn't charge a fee for executing extrinsics. I assume this is to make pallet tests easier since a lot of pallets modify your balance outside of the fees charged.
You can double-check this with this simple test:
#[test]
fn sandbox() {
ExtBuilder::default().build().execute_with(|| {
assert_eq!(Balances::free_balance(1), 0);
assert_ok!(System::remark(RuntimeOrigin::signed(1), vec![69, 69]));
});
}
I think you need to modify the mock runtime to actually charge fees (if that is even possible)
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 exactly? We are testing the correct execution of the logic within the extrinsics. For the "free execution" tests, we have an integration test that checks the validate and pre_dispatch execution. And I ran the extrinsic on zombienet to see if execution was free, and this worked. So I thing these three together should show the correct working of the pallet.
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 test is called user_can_dispense_for_free
but you are not testing that because there are no fees in this mock runtime, so if you think this is tested in other places like integration-tests, then I would remove this test and maybe leave a comment where this test can be found.
I see though that we have 2 dispenser tests called dispenser_signed_extensions_pass_for_new_account
and dispenser_works_with_runtime_values
But I don't see it testing that a user with 0 balance can call dispense, while other calls would require fees/ED.
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.
Love to see the feeless_if
+ JWT check in action!
The logic looks good, just few comments here and there that I would like to discuss before merging this into main
.
Also, the try-runtime-cli
is complaining about the Funding Pallet: Funding: On chain storage version StorageVersion(1) is set to non zero, while the pallet is missing the #[pallet::storage_version(VERSION)] attribute.
So maybe we should rebase this PR to include #223
What?
This PR introduces the dispenser-pallet, that can be used to get locked PLMC tokens for account with a valid credential.
Why?
To enable initial user growth on the Polimec network, we allow a certain amount of users to dispense tokens.
How?
The pallet has a pallet account that can be funded to distribute tokens. The token amount that can be dispensed is specified in the
DispenseAmount
storage item and can be set by an admin account using theset_dispense_amount
extrinsic. The initial amount is determined by the InitialDispenseAmount.A user can call the
dispense
extrinsic to dispense tokens. There are a couple of checks to see if the user is able to dispense.First, we check if the user has a valid credential and then we check if the Did in the credential has already dispensed tokens. We also check if the funding wallet still has tokens to distribute.
Signed Extensions for free execution
How do Signed Extensions work?
Every extrinsic that is submitted to a node, is first validated before it is added to the transaction pool and broadcasted to other nodes. The validation logic is written in Signed Extensions. If an extrinsic is valid, is being broadcasted to all other nodes. The validity of extrinsics is being tracked while it's in the transaction pool (it can get invalid by passing its mortality time for example). Once a collator starts building a block with this extrinsic, it first executes the pre_dispatch check, which should have similar logic as the validation check.
How to use SignedExtensions for free execution (in theory)?
Parity introduced the new
SkipCheckIfFeeless
signed extrinsic which is a wrapper around other signed extensions. A call can be marked as potentially free usingfeeless_if
(see dispense extrinsic). Thefeeless_if
implementation has some logic attached to see if the call is actually free. Iffeeless_if
returnstrue
,SkipCheckIfFeeless
skips the inner signed extension check. This can be used to skip theChargeTransactionPayment
extension, which is responsible for charging fees. So in Theory, just wrapping thisChargeTransactionPayment
extension withSkipCheckIfFeeless
and correctly implementingfeeless_if
, should make the call free.Free execution in practice?
SkipCheckIfFeeless
doesn't work out of the box. There were two problems we ran into:SkipCheckIfFeeless did not implement the validate function, which would skip the validation logic for fee payment for every transaction. So transactions were only checked for fee payment in the pre_dispatch function, which opens up a transaction pool DoS attack vector. We fixed this by implementing this Signed Extension ourselves with the validate function. Parity also implements it now: Fixes validation for
SkipCheckIfFeeless
extension paritytech/polkadot-sdk#3993.The CheckNonce extension has a check that the user paid for the Nonce storage by checking if the accounts providers and sufficients are non-zero. For new accounts these are zero, so we had to skip this check for the dispense call. So I implemented the CheckNonce signed extension myself, with this extra logic. Parity acknowledged this problem and is discussing a general solution at
CheckNonce
should not enforce the account creation requirement for feeless calls paritytech/polkadot-sdk#3991. I think we are fine with our current implementation, as users actually receive tokens that will be locked/vested over a period of 4 years and the accounts able to get the tokens are limited by the credentials.Testing?
Normal unit tests + a test to check the Signed extensions in the integration tests.
Screenshots (optional)
Anything Else?
Todo: