# Pallet Pass
Allows dispatching calls on behalf of a keyless account using an authenticator that resolves alternative signing methods.
## Overview
Historically, having an account on the Web3 Ecosystem and using it to send transactions over a network has been as easy as having a private key which can sign those. Now, this _ease_ comes with some tradeoffs. One of the most important is securely storing that key, to support signature operations.
Nonetheless, transactions are endorsed by accounts, not keys. A key is merely one means to get an `Account`. This pallet provides mechanisms for an `Account` to exist without necesarily hold a private key as traditionally known. This is done by introducing the concept of an `Authenticator`, very well known in the Web2 Ecosystem.
### Terminology
- An `Authenticator` is a **handler** that allows processing `Challenge`s to either gather access to the `Account`, or to register a new `Device` against the `Account`.
- A `Challenge` is the process where an `Authenticator` gathers a cryptographically verifiable input from a `Device`, and decides whether to provide access to an `Account`.
- The `Gas` is an unique **handler** that enables an `Account` to dispatch calls without it paying fees.
- A `Recovery` is a **handler** that allows registering a new `Device` when an `User` loses access to all their `Device`s.
- A `Registrar` is a **handler** to create a new `Account`. The rules for registering a new `Account` are defined by an `Origin`.
- A `Session` is the result of being granted to use an `Account` via using a `Device` on an `Authenticator`. Can be either:
- A short-lived session, where the access is granted a single time via a dispatchable that processes the authentication and dispatches the call, or
- A long-lived session, where an ephemeral key that is registered against the pallet and can function as a valid authentication method to gather access to an `Account` meanwhile the `duration` is not exceeded. The `duration` will be at most `MaxDuration`.
- An `User` is an entity that owns `Device`s to gather access to an `Account`. `User`s can recover an `Account` whenever a `Recovery` is enabled.
Additionally, there are some concepts bound to implementation mechanisms, like:
- A **handler** is a structure that implements a trait to enable one of the functions that the pallet must provide.
- An `Account` is an `AccountId` derived from a registering process (typically, an username), that can be used to dispatch calls on its behalf.
### Goals
This pallet supports four main purposes:
- Registering an `Account`, and providing access to it via an `Authenticator`.
- Dispatching calls to either unsigned extrinsics that pass the challenge for a given `Authenticator` (we call this short-live `Session`), or signed extrinsics previously registered on behalf of the pallet to control the `Account` for a limited time (we'll call this extended-sessions, and these signing keys `Device`s).
- Determining whether calling an extrinsic won't pay fees via the `Gas` handler.
- Recovering access to an `Account` via a `Recovery`.
## Interface
### Dispatchable Functions
- `register`/`claim`: Given the data requested by the `Registrar`, generates a unique hash for derivating an `Account`.
- `account_name`: An unique name for identifying the `Account`. Generally, an username.
- `authenticator`: An `Authenticator` registered on the pallet.
- `device_id`: An unique identification for the first `Device` attached to the `Account` against which to validate the authentication information.
- `maybe_session`: An optional `(ChallengePayload, SessionKey)`
- `authenticate`: Opens a long-lived `Session` using a `Device` and an `Authenticator`. Once it's opened, registers a `Session` against the `Account`.
- `account_name`: An unique name of the `Account`. Generally, an username.
- `authenticator`: An `Authenticator` registered on the pallet.
- `device_id`: An descriptor for the `Device` attached to the `Account` against which to validate the authentication information, that decodes to the type of device accepted by the `Authenticator`.
- `challenge_payload`: An encoded `BoundedVec<u8, T::MaxPayloadSize>` with the information required by the authenticator to validate the device.
- `session_key`: An `AccountId` to identify the `Session`.
- `add_device`: Requires being signed by an `AccountId` registered as a valid `Session` for the `Account`. Receives the information of a new device.
- `authenticator`: An `Authenticator` registered on the pallet.
- `device`: An descriptor for the `Device` attached to the `Account` against which to validate the authentication information, that decodes to the type of device accepted by the `Authenticator`.
- `dispatch`: Dispatches a call on behalf of an `Account` if the signer is a valid `Session` or the authentication details are valid. The fees, if any, will be paid by the `Account`, or by the signer in case there's not an `Account` tied to a signed origin.
- `call`: A valid `RuntimeCall` that can be dispatched on the runtime.
- `maybe_authentication`: An optional `(AccountName, Authenticator, DeviceId, )` tuple, sent to authenticate a device on-the-fly, producing a short-lived session.
- `maybe_next_session_key`: An optional `AccountId` for the next `Session` key (`AccountId`) that should be registered on behalf of the `Account`.
## Usage
### Registrar
```rs=
pub trait Registrar<AccountId, AccountName> {
// Required methods
fn is_claimable(account_name: &AccountName, claimer: &AccountId) -> types::RegistrarResult;
fn claim(account_name: &AccountName, claimer: &AccountId) -> types::RegistrarResult;
fn claimer_pays_fees(account_name: &AccountName, claimer: &AccountId) -> bool;
}
```
### Authenticator
```rs=
pub trait Authenticator<AccountId, AccountName, DeviceId> {
type Error: Parameter;
type Device: Parameter;
type Challenge: Parameter;
fn register_device(
&self,
account_name: AccountName,
device: &Device,
) -> DeviceId;
fn authenticate(
&self,
account_name: AccountName,
device_id: &DeviceId,
challenge: &Challenge,
) -> Result<(), Error>;
}
```
### Gas Handling
```rs=
pub trait GasHandler<AccountId> {
fn pays_fees(account_id: AccountId) -> bool;
}
```
```rs=
// ...
#[pallet::feeless_if(|origin: &OriginFor<T>, | -> bool {
let account_id = Self::authenticate(origin, ...);
!T::GasHandler::pays_fees(account_id)
})]
fn dispatch(
origin: OriginFor<T>,
call: Box<RuntimeCallFor<T>>,
) -> DispatchResult {
// ...
}
// ...
```
More info: [`#[pallet:feeless_if]`][attr:feeless_if]
```rs=
pub struct MembershipGasHandler<T>(PhantomData<T>);
impl<T: pallet_communities::Config> GasHandler<AccountIdOf<T>> for MembershipGasHandler<T> {
fn pays_fees(account_id: AccountIdOf<T>) -> bool {
// bring the membership for this account, if any
match T::Memberships::memberships_of(account_id) {
Some(membership) => {
// check the membership has a fe...
}
None => false
}
}
}
```
```rs=
impl pallet_pass::Config for Runtime {
// ...
type GasHandler = MembershipGasHandler<Self>;
// ...
}
```
### Prerequisites
## Assumptions
## Related Modules
[attr:feeless_if]: https://docs.rs/frame-support/latest/frame_support/pallet_macros/attr.feeless_if.html