Validate and execute
As previously stated, Starknet’s account structure is not completely arbitrary, but must include the following two functions, which account transactions call:
__execute__ functions guarantees payment to sequencers for work completed and protects them from Denial of Service (DoS) attacks.
__validate__ function ensures that any transaction submitted was indeed initiated by the account owner and therefore will not take up unjustified resources during the execution process.
Without this mechanism, a forged transaction can result in the sequencer stealing the user’s funds.
__validate__ ensures that the sequencer may only include transactions that were approved by the account owner.
The arbitrary logic allowed in the
__validate__ function gives the account’s designer the ability to determine what it means for a transaction to be valid. This gives rise to the usage of different signature schemes
and other exotic accounts.
__validate__ function fails, no fee will be taken from the account in question.
There are some limitations set on the
__validate__ function. The purpose of these limitations is twofold:
We want to avoid the sequencer having to do a lot of work only to discover that the validation failed and the sequencer is then not eligible to charge a fee (if this was possible, the sequencer would be completely exposed to DOS attacks). Validation, while now abstract and in control of the account owner rather than the protocol, should still be a simple operation. This is why a maximum steps limitation on the
__validate__function is currently in place on the Starknet network. For more information, see Current limits.
Even if the validation is simple, we could still face the following attack:
An attacker fills the mempool with transactions that are valid at time T.
A sequencer may start executing them, thinking that at the time he will produce his block, they will still be valid.
However, shortly after, at time T', the attacker sends one transaction that somehow invalidates all the previous ones and makes sure it’s included before the sequencer gets to publish his block (the attacker may do this by offering higher fees for this one transaction).
As a concrete example, think of many
__validate__ functions checking that the value of a storage slot is 1 and the attacker’s transaction later sets it to 0. To handle this issue, we add some further limitations.
Currently, Starknet enforces that
__validate__ does not call external contracts.
|The property that we achieve with the above restrictions is that a single storage update may only invalidate transactions from a single account (this is the best we can do, an account can always invalidate its own past transactions by changing its keys). Thus, the price (in fees) required to invalidate transactions in the mempool is linear in the number of unique accounts that we want to invalidate.
The purpose of the
__execute__ function is to abstract away the remaining actions performed by a transaction.
In Ethereum, a transaction is necessarily a call to a specific function in a smart contract. With the
__execute__ abstraction, the account designer controls the flow of the transaction. For example, multicalls can be natively supported in your account, saving the need to send multiple transactions (in practice, this is even harder to manage without multicalls due to nonces).