Just some general points about transaction mortality from my digging and understanding so far:
- The goal of mortality is to define when a transaction is valid from (must be some recent block hash/number) and roughly how many blocks it'll be valid for until it expires and can no longer be added to a block.
- There are two parts to mortality; first, the
Era
(Mortal
or Immortal
; the Mortal
variant containing a "phase" and "period" value), and second the "mortality checkpoint" (the block hash of the first block that the transaction is valid from).
- The
Era
is encoded into a compact 1 or 2 byte representation (for immortal or mortal respectively) and is a part of the "signed extra" details of the extrinsic. This compact encoding is important to save space in a block.
- The "mortality checkpoint" hash is a part of the signer payload; ie it's not a part of the extrinsic details, but the node must be able to reproduce it to verify the signature. The node uses the "phase" value to work out which block hash should be a part of the signer payload for verification.
With that in mind, let's get to the questions:
Not Manually
The Era::mortal
function takes two arguments, period
and current
(both u64s).
current
is the block number that you'd like the transaction to be valid from. This block number must point to the same block as the block hash provided in the "mortality checkpoint" signer payload argument. Probably it should be some recent block (ie fetching the latest finalized block for instance and using that block hash/number seems like a good default approach).
period
is the number of blocks after this block that the transaction will be valid for. It should be a power of 2 in the range [4,65536] (the Era::mortal
function will make this be the case if the value you provide is not already). The reason for this rounding-to-power-of-two is, I think, so that it can be compressed (along with the internal "phase" value) into just 2 bytes.
These two values are what you really care about when defining mortality; from which block is the tx valid from and after which block does it expire.
Manually
If you're not using the Era::manual(..)
function (eg because an equivalent is not available in the language you're using), personally my approach would be to copy the logic of it out into your own function. Take care to also copy the logic for how the Era
is SCALE encoded and decoded, if that's not handled for you (see here).
Internally, the Era::Mortal
stores a period
and phase
as u64s, and when being SCALE encoded or decoded, it mangles these into just 2 bytes.
period
has already been described above.
phase
is a value that a validator can use to work out which block hash it should expect in the signer payload in order to produce an identical signature to that which was given with the tx. See Era::mortal()
for how it's calculated.
The spec goes into more detail about the mortality phase and period: https://spec.polkadot.network/id-extrinsics#id-mortality