Key Data Consumers
Implementing a Key Data Consumer
As discussed in Using Keystore Accounts, the Keystore Validator (KV) connects the keystore to L2 smart accounts by facilitating proof verification of keystore-located signing data against state roots, and validating authentication data against the keystore-based signing data.
The first of these functions take place directly on the KV, while the validation of authentication data takes place in external contracts called Key Data Consumers (KDC). These contracts are responsible for defining how key data is parsed and used to validate authentications.
At transaction time, users must specify the address at which their desired KDC is deployed. An important artifact of the KDC is its CODEHASH
, which the KV will enforce matches the specified CODEHASH
in the data
from the keystore (see the KV’s expected data format here).
Implementing a Key Data Consumer
All KDCs must implement the following interface:
where
keyData
is the verified key dataauthData
is the authentication data to be validateduserOpHash
is the hash of the user operation
As an example, a key data consumer could implement m-of-n ECDSA signature verification logic. In this scenario:
keyData
would encode the thresholdm
and a list ofn
valid signersauthData
would be a list ofm
signatures authenticating a transactionuserOpHash
would be the digest signed by the signatures
As mentioned in Using Keystore Accounts, using the CODEHASH
to identify a piece of EVM execution allows for a (data, vkey)
pair to signal its desired on-chain validation logic. This allows deployments of the same key data consumer across different rollups to be identifiable without reference to a permissioned registry.
There are some considerations to keep in mind when using CODEHASH
to identify a piece of EVM execution. For example, consider a contract whose execution reads from a storage slot which can later be updated. In this case, the EVM bytecode committed to by CODEHASH
can behave differently depending on the storage value, which is not committed to by CODEHASH
.
The developer of the key data consumer must therefore ensure that the CODEHASH
upholds its intended semantic value. We strongly encourage developers to follow the guidelines below.
- The
consumeKeyData()
function should not read from storage slots which can later be updated (ideally, from no storage at all). - The contract should not perform external calls to addresses that are not precompiles.
- If you do perform an external call to a non-precompile address, check that
codesize != 0
.
- If you do perform an external call to a non-precompile address, check that
- Be careful with calling a precompile that is not supported on all networks. Low-level calls to empty addresses will silently succeed!
- If the expected behavior of the precompile on supported networks has
returndata
, check thatreturndatasize() != 0
.
- If the expected behavior of the precompile on supported networks has
The above is not an exhaustive list but extra care should be taken to implement the key data consumer around the assumption that it will be uniquely identified by its CODEHASH
.
While it cannot be enforced in EVM, the consumeKeyData()
function should be a pure function. If new functionality is required, it can be achieved by deploying a new key data consumer and initiating a corresponding update on the keystore. This approach ensures that each EVM validation of a keystore authentication is bound to a unique CODEHASH
and thus committed to in the keystore data.
Example Implementation
To see an example implementation of a key data consumer, see the Keystore Periphery.