Skip to content

keyless_account - [mainnet]

This module is responsible for configuring keyless blockchain accounts which were introduced in AIP-61.

use 0x1::bn254_algebra;
use 0x1::chain_status;
use 0x1::config_buffer;
use 0x1::crypto_algebra;
use 0x1::ed25519;
use 0x1::option;
use 0x1::signer;
use 0x1::string;
use 0x1::system_addresses;

Constants

A serialized BN254 G1 point is invalid.

const E_INVALID_BN254_G1_SERIALIZATION: u64 = 2;

A serialized BN254 G2 point is invalid.

const E_INVALID_BN254_G2_SERIALIZATION: u64 = 3;

The training wheels PK needs to be 32 bytes long.

const E_TRAINING_WHEELS_PK_WRONG_SIZE: u64 = 1;

Structs

Group

#[resource_group(#[scope = global])]
struct Group
Fields
dummy_field: bool

Resources

Groth16VerificationKey

The 288-byte Groth16 verification key (VK) for the ZK relation that implements keyless accounts

#[resource_group_member(#[group = 0x1::keyless_account::Group])]
struct Groth16VerificationKey has drop, store, key
Fields
alpha_g1: vector<u8>
32-byte serialization of alpha * G, where G is the generator of G1.
beta_g2: vector<u8>
64-byte serialization of alpha * H, where H is the generator of G2.
gamma_g2: vector<u8>
64-byte serialization of gamma * H, where H is the generator of G2.
delta_g2: vector<u8>
64-byte serialization of delta * H, where H is the generator of G2.
gamma_abc_g1: vector<vector<u8>>
\forall i \in {0, ..., \ell}, 64-byte serialization of gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * H, where H is the generator of G1 and \ell is 1 for the ZK relation.

Configuration

#[resource_group_member(#[group = 0x1::keyless_account::Group])]
struct Configuration has copy, drop, store, key
Fields
override_aud_vals: vector<string::String>
An override aud for the identity of a recovery service, which will help users recover their keyless accounts associated with dapps or wallets that have disappeared. IMPORTANT: This recovery service **cannot** on its own take over user accounts; a user must first sign in via OAuth in the recovery service in order to allow it to rotate any of that user's keyless accounts.
max_signatures_per_txn: u16
No transaction can have more than this many keyless signatures.
max_exp_horizon_secs: u64
How far in the future from the JWT issued at time the EPK expiry can be set.
training_wheels_pubkey: option::Option<vector<u8>>
The training wheels PK, if training wheels are on
max_commited_epk_bytes: u16
The max length of an ephemeral public key supported in our circuit (93 bytes)
max_iss_val_bytes: u16
The max length of the value of the JWT's iss field supported in our circuit (e.g., "https://accounts.google.com")
max_extra_field_bytes: u16
The max length of the JWT field name and value (e.g., "max_age":"18") supported in our circuit
max_jwt_header_b64_bytes: u32
The max length of the base64url-encoded JWT header in bytes supported in our circuit

Functions

new_groth16_verification_key

public fun new_groth16_verification_key(alpha_g1: vector<u8>, beta_g2: vector<u8>, gamma_g2: vector<u8>, delta_g2: vector<u8>, gamma_abc_g1: vector<vector<u8>>): keyless_account::Groth16VerificationKey
Implementation
public fun new_groth16_verification_key(alpha_g1: vector<u8>,
beta_g2: vector<u8>,
gamma_g2: vector<u8>,
delta_g2: vector<u8>,
gamma_abc_g1: vector<vector<u8>>
): Groth16VerificationKey {
Groth16VerificationKey {
alpha_g1,
beta_g2,
gamma_g2,
delta_g2,
gamma_abc_g1,
}
}

new_configuration

public fun new_configuration(override_aud_val: vector<string::String>, max_signatures_per_txn: u16, max_exp_horizon_secs: u64, training_wheels_pubkey: option::Option<vector<u8>>, max_commited_epk_bytes: u16, max_iss_val_bytes: u16, max_extra_field_bytes: u16, max_jwt_header_b64_bytes: u32): keyless_account::Configuration
Implementation
public fun new_configuration(
override_aud_val: vector<String>,
max_signatures_per_txn: u16,
max_exp_horizon_secs: u64,
training_wheels_pubkey: Option<vector<u8>>,
max_commited_epk_bytes: u16,
max_iss_val_bytes: u16,
max_extra_field_bytes: u16,
max_jwt_header_b64_bytes: u32
): Configuration {
Configuration {
override_aud_vals: override_aud_val,
max_signatures_per_txn,
max_exp_horizon_secs,
training_wheels_pubkey,
max_commited_epk_bytes,
max_iss_val_bytes,
max_extra_field_bytes,
max_jwt_header_b64_bytes,
}
}

validate_groth16_vk

Pre-validate the VK to actively-prevent incorrect VKs from being set on-chain.

fun validate_groth16_vk(vk: &keyless_account::Groth16VerificationKey)
Implementation
fun validate_groth16_vk(vk: &Groth16VerificationKey) {
// Could be leveraged to speed up the VM deserialization of the VK by 2x, since it can assume the points are valid.
assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G1, bn254_algebra::FormatG1Compr>(&vk.alpha_g1)), E_INVALID_BN254_G1_SERIALIZATION);
assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G2, bn254_algebra::FormatG2Compr>(&vk.beta_g2)), E_INVALID_BN254_G2_SERIALIZATION);
assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G2, bn254_algebra::FormatG2Compr>(&vk.gamma_g2)), E_INVALID_BN254_G2_SERIALIZATION);
assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G2, bn254_algebra::FormatG2Compr>(&vk.delta_g2)), E_INVALID_BN254_G2_SERIALIZATION);
for (i in 0..vector::length(&vk.gamma_abc_g1)) {
assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G1, bn254_algebra::FormatG1Compr>(vector::borrow(&vk.gamma_abc_g1, i))), E_INVALID_BN254_G1_SERIALIZATION);
};
}

update_groth16_verification_key

Sets the Groth16 verification key, only callable during genesis. To call during governance proposals, use set_groth16_verification_key_for_next_epoch.

WARNING: See set_groth16_verification_key_for_next_epoch for caveats.

public fun update_groth16_verification_key(fx: &signer, vk: keyless_account::Groth16VerificationKey)
Implementation
public fun update_groth16_verification_key(fx: &signer, vk: Groth16VerificationKey) {
system_addresses::assert_aptos_framework(fx);
chain_status::assert_genesis();
// There should not be a previous resource set here.
move_to(fx, vk);
}

update_configuration

Sets the keyless configuration, only callable during genesis. To call during governance proposals, use set_configuration_for_next_epoch.

WARNING: See set_configuration_for_next_epoch for caveats.

public fun update_configuration(fx: &signer, config: keyless_account::Configuration)
Implementation
public fun update_configuration(fx: &signer, config: Configuration) {
system_addresses::assert_aptos_framework(fx);
chain_status::assert_genesis();
// There should not be a previous resource set here.
move_to(fx, config);
}

update_training_wheels

#[deprecated]
public fun update_training_wheels(fx: &signer, pk: option::Option<vector<u8>>)
Implementation
public fun update_training_wheels(fx: &signer, pk: Option<vector<u8>>) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
chain_status::assert_genesis();
if (option::is_some(&pk)) {
assert!(vector::length(option::borrow(&pk)) == 32, E_TRAINING_WHEELS_PK_WRONG_SIZE)
};
let config = borrow_global_mut<Configuration>(signer::address_of(fx));
config.training_wheels_pubkey = pk;
}

update_max_exp_horizon

#[deprecated]
public fun update_max_exp_horizon(fx: &signer, max_exp_horizon_secs: u64)
Implementation
public fun update_max_exp_horizon(fx: &signer, max_exp_horizon_secs: u64) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
chain_status::assert_genesis();
let config = borrow_global_mut<Configuration>(signer::address_of(fx));
config.max_exp_horizon_secs = max_exp_horizon_secs;
}

remove_all_override_auds

#[deprecated]
public fun remove_all_override_auds(fx: &signer)
Implementation
public fun remove_all_override_auds(fx: &signer) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
chain_status::assert_genesis();
let config = borrow_global_mut<Configuration>(signer::address_of(fx));
config.override_aud_vals = vector[];
}

add_override_aud

#[deprecated]
public fun add_override_aud(fx: &signer, aud: string::String)
Implementation
public fun add_override_aud(fx: &signer, aud: String) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
chain_status::assert_genesis();
let config = borrow_global_mut<Configuration>(signer::address_of(fx));
vector::push_back(&mut config.override_aud_vals, aud);
}

set_groth16_verification_key_for_next_epoch

Queues up a change to the Groth16 verification key. The change will only be effective after reconfiguration. Only callable via governance proposal.

WARNING: To mitigate against DoS attacks, a VK change should be done together with a training wheels PK change, so that old ZKPs for the old VK cannot be replayed as potentially-valid ZKPs.

WARNING: If a malicious key is set, this would lead to stolen funds.

public fun set_groth16_verification_key_for_next_epoch(fx: &signer, vk: keyless_account::Groth16VerificationKey)
Implementation
public fun set_groth16_verification_key_for_next_epoch(fx: &signer, vk: Groth16VerificationKey) {
system_addresses::assert_aptos_framework(fx);
config_buffer::upsert<Groth16VerificationKey>(vk);
}

set_configuration_for_next_epoch

Queues up a change to the keyless configuration. The change will only be effective after reconfiguration. Only callable via governance proposal.

WARNING: A malicious Configuration could lead to DoS attacks, create liveness issues, or enable a malicious recovery service provider to phish users’ accounts.

public fun set_configuration_for_next_epoch(fx: &signer, config: keyless_account::Configuration)
Implementation
public fun set_configuration_for_next_epoch(fx: &signer, config: Configuration) {
system_addresses::assert_aptos_framework(fx);
config_buffer::upsert<Configuration>(config);
}

update_training_wheels_for_next_epoch

Convenience method to queue up a change to the training wheels PK. The change will only be effective after reconfiguration. Only callable via governance proposal.

WARNING: If a malicious key is set, this could lead to stolen funds.

public fun update_training_wheels_for_next_epoch(fx: &signer, pk: option::Option<vector<u8>>)
Implementation
public fun update_training_wheels_for_next_epoch(fx: &signer, pk: Option<vector<u8>>) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
// If a PK is being set, validate it first.
if (option::is_some(&pk)) {
let bytes = *option::borrow(&pk);
let vpk = ed25519::new_validated_public_key_from_bytes(bytes);
assert!(option::is_some(&vpk), E_TRAINING_WHEELS_PK_WRONG_SIZE)
};
let config = if (config_buffer::does_exist<Configuration>()) {
config_buffer::extract_v2<Configuration>()
} else {
*borrow_global<Configuration>(signer::address_of(fx))
};
config.training_wheels_pubkey = pk;
set_configuration_for_next_epoch(fx, config);
}

update_max_exp_horizon_for_next_epoch

Convenience method to queues up a change to the max expiration horizon. The change will only be effective after reconfiguration. Only callable via governance proposal.

public fun update_max_exp_horizon_for_next_epoch(fx: &signer, max_exp_horizon_secs: u64)
Implementation
public fun update_max_exp_horizon_for_next_epoch(fx: &signer, max_exp_horizon_secs: u64) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
let config = if (config_buffer::does_exist<Configuration>()) {
config_buffer::extract_v2<Configuration>()
} else {
*borrow_global<Configuration>(signer::address_of(fx))
};
config.max_exp_horizon_secs = max_exp_horizon_secs;
set_configuration_for_next_epoch(fx, config);
}

remove_all_override_auds_for_next_epoch

Convenience method to queue up clearing the set of override aud’s. The change will only be effective after reconfiguration. Only callable via governance proposal.

WARNING: When no override aud is set, recovery of keyless accounts associated with applications that disappeared is no longer possible.

public fun remove_all_override_auds_for_next_epoch(fx: &signer)
Implementation
public fun remove_all_override_auds_for_next_epoch(fx: &signer) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
let config = if (config_buffer::does_exist<Configuration>()) {
config_buffer::extract_v2<Configuration>()
} else {
*borrow_global<Configuration>(signer::address_of(fx))
};
config.override_aud_vals = vector[];
set_configuration_for_next_epoch(fx, config);
}

add_override_aud_for_next_epoch

Convenience method to queue up an append to the set of override aud’s. The change will only be effective after reconfiguration. Only callable via governance proposal.

WARNING: If a malicious override aud is set, this could lead to stolen funds.

public fun add_override_aud_for_next_epoch(fx: &signer, aud: string::String)
Implementation
public fun add_override_aud_for_next_epoch(fx: &signer, aud: String) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
let config = if (config_buffer::does_exist<Configuration>()) {
config_buffer::extract_v2<Configuration>()
} else {
*borrow_global<Configuration>(signer::address_of(fx))
};
vector::push_back(&mut config.override_aud_vals, aud);
set_configuration_for_next_epoch(fx, config);
}

on_new_epoch

Only used in reconfigurations to apply the queued up configuration changes, if there are any.

public(friend) fun on_new_epoch(fx: &signer)
Implementation
public(friend) fun on_new_epoch(fx: &signer) acquires Groth16VerificationKey, Configuration {
system_addresses::assert_aptos_framework(fx);
if (config_buffer::does_exist<Groth16VerificationKey>()) {
let vk = config_buffer::extract_v2();
if (exists<Groth16VerificationKey>(@aptos_framework)) {
*borrow_global_mut<Groth16VerificationKey>(@aptos_framework) = vk;
} else {
move_to(fx, vk);
}
};
if (config_buffer::does_exist<Configuration>()) {
let config = config_buffer::extract_v2();
if (exists<Configuration>(@aptos_framework)) {
*borrow_global_mut<Configuration>(@aptos_framework) = config;
} else {
move_to(fx, config);
}
};
}

Specification

pragma verify=false;