πLocksmith
Providing the Trust Model
Design Ethos
The Locksmith contract is dependent on a functioning and compatible KeyVault. Once deployed, the KeyVault is then set by its deployer to "respect" the Locksmith's supply management wishes. In this way, the "Locksmith" becomes the minter of any and all keys.
With full single-threaded access to key management, the Locksmith exposes a singular "Trust" model for each key. New trusts are created when a user requests one and is minted a root key.
It operates simiarly as a UUPSUpgradeable and can be locked after development is complete. It stores the information about the trust, the keys that belong to it, and some metadata about the keys.
Root key holders can interact with the Locksmith by calling the operations. For each operation except createTrustAndRootKey the caller must possess a valid Locksmith root key.
Storage
The main contract storage is the registry of the Trust. Trusts have their own ID structure, and contain all of the metadata about the trust and associated keys.
address public keyVault;
struct Trust {
uint256 id;
bytes32 name;
uint256 rootKeyId;
EnumerableSet.UintSet keys;
mapping(uint256 => bytes32) keyNames;
}
mapping(uint256 => Trust) private trustRegistry;
uint256 private trustCount;
mapping(uint256 => uint256) private keyTrustAssociations;
uint256 private keyCount;keyVault
The Locksmith contract is deployed with an address pointing to a KeyVault on initialization. This is the only KeyVault the Locksmith will use to manage his key requests.
struct Trust
id
The trust's global unique identifier.
name
Stores as a byte-encoded bytes32, a short descriptive name that does not require global uniqueness.
rootKeyId
Each trust has a single root key Id that can never change. This field denotes the key ID that represents the root key for that trust model instance.
keys
The array of key IDs that belong to the trust. Will always contain the root key ID.
keyNames
A mapping of key IDs to their human readable names encoded as a bytes32.
trustRegistry
A mapping from a trust's ID to the Trust structure.
trustCount
The number of trusts the locksmith has in it's registry.
keyTrustAssociations
Each key belongs to a trust, but it isn't easy to find out which one from an array of Trust structures. An additional mapping keeps this unique piece of key metadata to access the trust it belongs to.
keyCount
An internal state variable that determines immediately what is considered a "valid key" by keeping track of the number of unique keys in existence.
Operations
There are introspection methods, that will not get extensive review here. They are getKeyVault(), getTrustInfo(uint256 trustId), getKeys(uint256 trustId), inspectKey(uint256 keyId). These methods supply the key vault's contract address, the Trust metadata stored in the struct, the list of keys and their data as described in the previous section.
createTrustAndRootKey
This is the method any message sender can call to immediately create a trust and send the root key to the designated recipient. It only takes a trust name, and a destination address. The mint function is an internal one that adds the given key id (in this case, the root key), optionally soulbinds the receiver before minting, and then minting the new key.
isRootKey
Definitely answers if a key is a considered root for a valid trust by making sure the key ID itself is valid, the root key matches the one determined in the trust association index, and that the key is on the trust's ring list.
createKey
The holder of a root key can use this method to generate brand new keys and add them to the root key's associated trust key ring. By default, the generated keys will have no attached permissions or special features. The internal method getTrustFromRootKey checks to ensure the message sender holds the root key used for the operation, and that the key is in fact root. If either are not true the transaction will revert. It will otherwise provide the trustId in return.
This method can only be successfully called by a root key holder.
copyKey
The root key holder can call this method if they have an existing key type within their trust they want to copy. This method will work even if the key is "extinct" in that all previous copies were burnt or lost. Having multiple instances of the same key can be very useful and flexible.
This method can only be successfully called by a root key holder.
soulbindKey
This method can be called by a root key holder to make a key soulbound to a specific walet. When soulbinding a key, it is not required that the target address yet holds that specific key. The amount set ensures that when sending a key of a specific type, that they hold at least the amount that is bound to them after the transaction.
This method can only be successfully called by a root key holder.
burnKey
The root key holder can call this method if they want to revoke a key from a current holder. This function alone makes the Locksmith NFTs utilitarian on behalf of the root key holder and not speculative in nature.
This method can only be successfully called by a root key holder.
validateKeyRing
A method that determines if a set of keys all belong to the same trust. Optionally enables you to revert if a root key is in the list.
Last updated