Key Data Consumers
Implementing a Key Data Consumer
As discussed in Using Keystore Accounts, the Keystore Validator (KV) is the central smart contract connecting the keystore to smart accounts on L2s. The KV’s main functions are to persist keystore state roots, facilitate proof verification of keystore-located signing data against the state roots, and validate authentication data against the keystore-based signing data.
The first two of these functions take place in functions on the KV, while validation of authentication data takes place in external contracts called Key Data Consumers (KDC) which are deployed through and registered in the KV. These contracts are responsible for defining how key data is parsed and used to validate authentications.
The KV maintains a registry of KDCs that can be permissionlessly updated. For KDC deployment and registration, the KV exposes the following interface:
For a KDC to be available for use to the KV, it must be deployed and registered via the above function. An important artifact of the deployment is the creation codehash, defined by
which is used as the key of a mapping from creationCodehash
to deployedAddress
and will be referenced in the validation flow against the Keystore Validator.
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 creationCodehash
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 creationCodehash
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 creationCodehash
can behave differently depending on the storage value, which is not committed to by creationCodehash
.
The developer of the key data consumer must therefore ensure that the creationCodehash
upholds its intended semantic value. We strongly encourage developers to follow the guidelines below.
- Creation code execution should not return runtime code that is not a subset of the creation code.
- The
consumeKeyData()
function should not read storage slots which can later be updated. - 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 creationCodehash
.
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 creationCodehash
and thus committed to in the keystore data.
Example Implementation
To see an example implementation of a key data consumer, see the Keystore Periphery.