Skip to content

Instantly share code, notes, and snippets.

@banool
Created October 30, 2023 13:00
Show Gist options
  • Save banool/bdf4ee28ac1ee0de17b67be054d4d165 to your computer and use it in GitHub Desktop.
Save banool/bdf4ee28ac1ee0de17b67be054d4d165 to your computer and use it in GitHub Desktop.
aip title authors discussions-to (*optional) Status type created
36
Universally Unique Identifiers
Draft
Standard
06/01/2023

AIP 36 - Universally Unique Identifiers

Summary

This AIP proposes to add two new native functions generate_unique_address_internal and get_txn_hash in Aptos framework. The generate_unique_address_internal generates and outputs a unique 256-bit identifier (of type address) for each function call. The get_txn_hash function outputs the hash of the current transaction. The generate_unique_address_internal function calls can run efficiently and in parallel. In other words, when two transactions run generate_unique_address_internal method, they can be executed in parallel without any conflicts. Initially, we will use these unique identifiers internally as addresses for newly created Move Objects, specifically in Token V2 minting. We also propose to add a wrapper function called generate_auid which wraps the unique address generated by the generate_unique_address_internal in the non-copy-able AUID struct type, guaranteeing that every two instances of AUID value are different.

Motivation

There is a general need to be able to create unique identifiers or addresses. There is no such utility in Move today, and so various alternatives have been used, which bring performance implications. We want to provide such a utility for all usecases that needed it.

Concretely, when a new object is created, we need to associate it with a unique address. For named objects, we deterministically derive it from the name. But for all other objects, we currently derive it from a GUID (Globally Unique Identifier) that we create on the fly. A GUID consists of a tuple (address, creation_num). We create GUID by having address be the account address of the object or resource’s creator, and the creation_num is the guid sequence number of the object or resource created by that account. As the sequence number creation_num has to be incremented for each object/resource creation, GUID generation is inherently sequential, within the same address. As an example, in Token V2 whenever a new token is minted using token::create_from_account, object is created to back it, which uses GUID generated based on the collection address, and so all such mints from the same collection are inherently sequential.

This AIP thereby creates a new type of identifier called UUID (Universally Unique Identifier). This is a 256-bit identifier that is universally unique amongst all the identifiers generated by all the accounts for all purposes (and are different from other methods generating identifiers that use domain separation from Scheme in authenticator.rs). We propose adding two native functions create_unique_address and get_txn_hash in Aptos framework. Every time a MOVE code calls create_unique_address, the function outputs a universally unique 256-bit value (of type address). The function creates a unique address by using the hash of the current transaction. As exposing the transaction hash might have more applications in the future, we also propose to add the get_txn_hash function which returns the hash of the current transaction.

We additionally create object::create_object and token::create_token functions, utilizing new utility functions, to provide more efficient untracked token minting. Those deprecate object::create_object_from_account, object::create_object_from_object and token::create_from_account functions.

We additionally propose to add create_uuid Move function, which calls the create_unique_address function and outputs a struct UUID containing the unique address. The address field of the UUID struct cannot be modified by other Move modules, nor the struct can be copied. As the only way of creating a UUID struct is by calling the create_uuid method, any code that receives a UUID struct as input can be confident that the underlying address is uniquely generated, and no two instances of UUID will match.

Specification

The Aptos Framework contains a transaction_context module, which can be used by MOVE modules to retrieve information related to the current transaction being executed. When a transaction is executed, the session first creates a context. The context contains, amongst other things, NativeTransactionContext . The transaction_context module provides an API interface to answer questions on NativeTransactionContext .

For this AIP, when a session is created, we will store the transaction hash in the NativeTransactionContext. We will additionally store a uuid_counter in the context and initiate the counter to 0. We will add a native function called create_unique_address in transaction_context module. When called, the create_unique_address function will first increment the uuid_counter and outputs a value in the below format

output identifier = SHA3-256(txn_hash || uuid_counter || 0xFB)

For domain separation, new entry (0xFB) is added into Scheme in authenticator.rs, which is appended to the end of the SHA3-256 input for domain separation, making sure to generate different identifiers from mechanisms and purposes defined there. Formula is equivalent to how output identifier is created from Guid(txn_hash, uuid_counter) in the 0xFB namespace.

Hash of SessionId is used as txn_hash. Each user transaction contains the sender’s account information and sequence number that is incremented for each transaction. For BlockMetadata transaction, hash of BlockData is used (which has epoch and round number inside). And there should be only one genesis transaction. This means each transaction accepted by Aptos consensus has unique set of bytes, and so produces unique transaction hash (assuming no SHA3-256 collisions).

Alternatives

Alternative place to do domain separation would be to have each callsite do their own domain separation. object.move does domain separation for itself for existing uses, and it might be cleaner from it's perspective to keep UUID domain separation there. But that would require all other callers to deal with domain separation, and so we've added a commend into object.move to make it clear the same domain separation is happening in native code instead. If someone needs domain separation from a method that is not aligned with authenticator.rs, they can do additional domain separation on top of the uuid returned.

Another alternative to the above create_unique_address function, is to expose the get_txn_hash and get_txn_unique_seq_num native functions, and do hashing in move.

Reference Implementation

This feature is currently implemented in the PR aptos-labs/aptos-core#8401.

Notes

Above function produces unique identifiers that have no collisions within them (given no SHA3-256 collisions), or across other ways to generate unique addresses captured in authenticator.rs - like all current GUID flows. But it isn’t “attacker-proof” (neither are the current GUID flows), i.e. if someone knows transaction hash, it can front-run and create a transaction that knows that object address. So in order to guarantee no collisions, scoping of who can create resources and not allowing input addresses can provide such guarantee. (i.e. object.move doesn’t provide a function to create an object at a given input address, it always internally computes it).

Generated identifiers are random-looking, and only way to retrieve them later is to store their address from where you need to access them (or use events during indexing for of chain analysis). In contrast, creating uuids from names is a repeatable process, and can be looked up later without indexing of the handle. For this reason, at this time, we are replacing all functions within object.move except for create_named_object to use above utility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment