Clients
Once a smart contract wallet has integrated Keyspace support, the client software needs to add support as well.
Sending Transactions
Transactions for Keyspace wallets are authorized by the user's signature alongside a proof of the wallet's current configuration in Keyspace. Keyspace's mksr_proof
RPC call retrieves the needed proof from a Keyspace node.
export async function signAndWrap(
{ hash, privateKey, keyspaceKey }: { hash: Hex; privateKey: Hex; keyspaceKey: Hex }
): Promise<Hex> {
const signature = await sign({ hash, privateKey });
const publicKey = secp256k1.getPublicKey(privateKey.slice(2), false);
const pk256 = serializePublicKeyFromBytes(publicKey);
const dataHash = getDataHash(pk256);
const configProof = await getKeyspaceConfigProof(keyspaceClient, keyspaceKey, vkHashEcdsaAccount, dataHash);
return encodeSignature({
signature,
publicKey,
configProof: configProof.proof,
});
}
Changing Keys
Changes to owner keys are written to Keyspace instead of the smart contract itself. The smart contract picks up key changes through the configuration proofs that are provided with each transaction and are verified against the latest Keyspace state root.
To change the configuration stored in Keyspace, the user needs to sign the new configuration, then generate a zero-knowledge proof with the appropriate Account
circuit for their wallet to demonstrate to Keyspace that they're authorized to make the change.
Sign the New Configuration
Account
proofs require a signature of the newKey
. This isn't a "new key," it's a commitment to the new configuration to be stored in the Keyspace record, and this commitment is constructed in the same manner as the original key for the wallet.
const dataHash = getDataHashSecp256k1(newPrivateKey);
const newKey = getKeyspaceKey(vkHashEcdsaAccount, dataHash);
const newKey254 = toHex(fromHex(newKey, "bigint") >> BigInt(2), { size: 32 });
const signature = await sign({ hash: newKey254, privateKey: currentPrivateKey });
const signatureData = encodePackedSignature(signature);
Generate an Account
Circuit Proof
With a signature of the newKey
, anyone can generate the Account
proof needed to change the key. The proving key for these circuits can be quite large: our EcdsaAccount
circuit has a 96MB proving key:
-rw-r--r-- 1 65M Mar 13 14:57 51baa0cc62607033629f491a79cca59244f2d3b4d122d5dabb4f5c3d18a35155.ccs
-rw-r--r-- 1 96M Mar 13 14:57 51baa0cc62607033629f491a79cca59244f2d3b4d122d5dabb4f5c3d18a35155.pk
-rw-r--r-- 1 48K Mar 13 14:57 51baa0cc62607033629f491a79cca59244f2d3b4d122d5dabb4f5c3d18a35155.vk
Other than the size, the other obstacle to letting users generate their own proofs is that it isn't straightforward to compile gnark circuits into the WebAssembly we need for JavaScript clients.
Instead, we expect wallet vendors to run their own wallet support services that includes an mksr_recover
RPC call for their users to use.
With that service available, the client can fetch the zero-knowledge proof they need to change their key:
const recoverResult = await recoveryClient.getSignatureProof({
key,
newKey,
circuitType,
signature: signatureData,
});
mksr_recover
returns the proof, current verification key, and the current data in the wallet's Keyspace record.
Set the New Configuration
With those values, you have everything you need for an mksr_set
call, which sends your proof to the sequencer and updates the Keyspace state on your behalf.
await keyspaceClient.setConfig({
key,
newKey,
...recoverResult,
});
Keyspace RPC API
mksr_proof
: generate a state proof for a given key
Parameters:
key
: keystore keyvkHash
: keccak256(verification key) >> 8dataHash
: keccak256(account data) >> 8
Example:
curl -H "Content-Type: application/json" https://sepolia-alpha.key.space \
-d '{"id":0,"jsonrpc":"2.0","method":"mksr_proof","params":["0x41f3582833f849b9cbe51c7eb3e86effde1cebf94af99fc752fab6481fb0cfc","0x7d36e133ab7f897fb8f97dec38a69fb083f5cd282717da9a0bc51986cc19b7","0x96b36c7046c67e8165814c3bd34ea0f13e5e731228b362e1efae54980b3107"]}'
{"jsonrpc":"2.0","id":0,"result":{"root":"0x18390ea8ab0886dc4f22fb59d9a6d9dea4b08bcc2b0a8cc7d33fcc629a96bf17","proof":"0x18b530c9d3ee3dd18f63c389214dd6184c52f0d04a7ad57344e3866484da9176018be4eddf31baf53ee8114176e951ff25863f9c821bfc320e9f485f9f896f1401cd357010362155c26f1f66b48ba3f68b6bde1bd20c965b36831e465a43f10f0d7064759a612f76bcc3adae92b120a70d86951124382084d95959ce7aa8a9fa21c8205c0e66620819586fda21cf891ef1ce0a431cae624252f855fa6148d94400b7ddf3a66b8916875dded66a115ff1563f9c66b3cc68cb93f0afab9acb57a8228ca9a0a2a22789dee97fb95134807b822bc443712418a369a9eecb0ce06002035250bd849b8542d5ed1a3ac467b9959a21410549dd0c015afdf95c77ffff9306f8fd963b71aadf1fa3456a6863c014f4f2dc338af2ea2d0ff0cb9c643de07b123e67e3ccae365c03bb1410501858e89595e9f618d7dc9b88acde7122630e860609a20533a5fdb8acdb27064eb28c33be26fc57fa888d0421e3c46deccf08d92fd5d642502551b96911886a815abedab9dca15656468c8cdb58c74898d3c1c91c1dc379dc6fa2c23b453894064449206c41c9dce7b77cf84ba2b311890cd4cd0cabad00dfd4d96b5440479ece010b804fc0cdd01bccd7a0308e33f434c294e722ec0a634d83b68a11aef609be4a877f31ed7557ba39363a412fc126a0af989329492f34f1cf465d7a46af852614e22e4ee7cad6926a6cae5d489744b480e44b2a6524c084010be3df090f77950187fff00afdd93dfefa1f7a7a479bfbcab3060d3d11cd27939f79f0f3c6c2934c77ae05544e1b9460c64e2efea80f0be5d35f13bbd8120c395f7dd33f2965edd6fa09c7f06a54153da468aa19679f5b8022951a9d7e8998c01ce9a77e570eff517218803776bddd594a846fcef5c8fe60ccff028fbfd580280d4c0b9b03fdeaec34f7a3157d562fb10fafa7444200b74405582d317cc06cc9eb145d591d94ea5dbb5dd450d5e2126c94cdd54e233231eb2db10b3b7e2b526025c97de8c1d2c4fdbdc5d59138d0ee156f8a6292a882bb3b81190f7b9e6339fa7ee916b73311e8bbeb06b0f20032dbfca219cc3010a341f455282b03c2f5682aa74062911f2f4821f3c65ba3e390f6a58234061732aec43ad3f913b2df2599b7c894e78570fab30893e0047210e537a9d6715f1c3a22b56e9d3a"}}
mksr_get
: get the current value for a given key
Parameters:
key
: keystore key
Example:
curl -H "Content-Type: application/json" https://sepolia-alpha.key.space \
-d '{"id":0,"jsonrpc":"2.0","method":"mksr_get","params":["0x48245f681e532926c9435f6aed714e368cea1070ccf9c77352fbb1dc42fe232"]}'
{"jsonrpc":"2.0","id":0,"result":"0x48245f681e532926c9435f6aed714e368cea1070ccf9c77352fbb1dc42fe232"}
mksr_set
: change the current value for a given key
Parameters:
key
: keystore keynewKey
: new keystore keycurrentVk
: verification keycurrentData
: account dataproof
: state proof
Example:
curl -H "Content-Type: application/json" https://sepolia-alpha.key.space \
-d '{"id":0,"jsonrpc":"2.0","method":"mksr_set","params":["0x48245f681e532926c9435f6aed714e368cea1070ccf9c77352fbb1dc42fe232","0x1c882cabbc2303d8a1eb20e62a35b40d5378ecdbc1ed71ed41f659da1d0d4c31","0x...","0x...","0x..."]}'
{"jsonrpc":"2.0","id":0,"result":null}