Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions AZIPs/azip-10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# AZIP-10: Rename Tagging Key

## Preamble

| `azip` | `title` | `description` | `author` | `discussions-to` | `status` | `category` | `created` |
|-----|-|-|-|-|-|-|-|
| 10 | Rename Tagging Key | Rename the unused tagging-key slot in the contract instance to signing key | Ilyas Ridhuan (@IlyasRidhuan) | - | Draft | Core | 2026-04-29 |


## Abstract
Aztec's `PublicKeys` struct currently includes a tagging public key (`tpk`) and a corresponding tagging secret key (`tsk`). However, tagging for note discovery is in fact currently derived from the sender's `ivsk` and the `tsk` is unused (see [Rationale](#Rationale)).

This AZIP proposes renaming `tpk` / `tsk` to `spk` / `ssk` (signing public key / signing secret key) to semantically align it with its planned future use as a means for contracts to sign messages.

## Impacted Stakeholders

This AZIP changes a struct field name and the secret-key derivation domain separator, so every stakeholder that consumes, displays, or persists the `PublicKeys` struct or its derived keys is affected.

### App Developers
Noir contract authors who consume `get_public_keys(account)` see the field rename `tpk` → `spk`. Existing contracts that reference `tpk_m`, `TpkM`, `tagging_key`, or related identifiers will not compile and must be updated.

### Infrastructure Providers (Indexers, P2P Nodes, Block Explorers)
Decoders that reference fields by name MUST be updated.

### Wallets
The `PublicKeys` struct field name changes from `tpk` / `tpk_hash` to `spk` / `spk_hash`.
Wallets need to update their derivation of secrets and addresses.

While wallets that persist the secret keys directly (i.e. store `tsk` rather than re-deriving it from a seed) can carry their existing keys forward and preserve the account address, this is not recommended as it is fragile to legacy code paths.


## Motivation

A dedicated key committed to an Aztec address that can be used by contracts to sign messages on behalf of the contract owner is a useful construct. This key is not a replacement for the tx authorisation mechanism (e.g. AuthWits) and should only be used to sign
non-state modifying transactions.

## Specification

> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

### Overview of Renames

| Before | After |
|--------|-------|
| `tpk` / `Tpk` | `spk` / `Spk` |
| `TpkM` (struct) | `SpkM` (struct) |
| `tpk_m`, `tpk_m_hash` | `spk_m`, `spk_m_hash` |
| `tsk`, `tsk_m` | `ssk`, `ssk_m` |
| `DOM_SEP__TSK_M` | `DOM_SEP__SSK_M` |
| `DEFAULT_TPK_M_X` / `_Y` | `DEFAULT_SPK_M_X` / `_Y` |
| `masterTaggingPublicKey` | `masterSigningPublicKey` |
| `masterTaggingSecretKey` | `masterSigningSecretKey` |

### Change to the `PublicKeys` Struct
The protocol-circuits `PublicKeys` struct SHALL be:

```noir
pub struct PublicKeys {
pub nhpk: NhpkM,
pub ivpk: IvpkM,
pub ovpk: OvpkM,
pub spk: SpkM, // was: TpkM
}
```

### Change to the Secret-Key Derivation Domain Separator
The domain separator MUST be renamed `DOM_SEP__TSK_M` → `DOM_SEP__SSK_M`.

```noir
DOM_SEP__SSK_M = poseidon2_hash_bytes(b"az_dom_sep__ssk_m")
```
### Change to the Contract Instance and Event Payload
The `ContractInstance` and `ContractInstancePublished` struct field referencing `TpkM` MUST be renamed.

### Change to the Oracle Interface
The oracle that returns public keys to a private function MUST return the renamed `spk` field in place of `tpk`. Identifier strings used as oracle keys MUST be updated.

### Change to Kernel Circuits
Kernel circuits that validate the contract address of the function call being processed MUST reference `SpkM` when re-deriving and checking `public_keys_hash`. The hashing inputs and output are unchanged.

## Rationale

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### What is the tagging key for?
The tagging key is a 3-year-old concept that was intended to help a recipient more quickly brute-force-identify which logs are pertinent to them. The idea is explained [here](https://ethereum-magicians.org/t/erc-5564-stealth-addresses/10614/4). But it quickly fell out of favour:
- It was only slightly more computationally efficient than the approach taken in the finalised Stealth Addresses EIP, and it was actually less efficient to download. This bandwidth efficiency is the primary bottleneck to brute-forcing, so the tagging public key idea was strictly worse.
- In most cases, it is assumed that a sender and recipient can interact before any Aztec transactions, so they can pre-establish a shared secret, which removes the need to brute-force, and hence reduces the need for any brute-forcing optimisations at the protocol level.
- (Note: some use cases will _still_ require non-interactivity: that the sender must convey a shared secret to the recipient via the blockchain without any prior interactions. In such cases, brute-forcing of an initial shared secret will still be needed, but the tagging public key would still not be helpful in improving such brute-forcing).
Some mistake the intented usage of the tagging keypair as the keypair that should be used to establish a tagging shared secret: that was never the case. The convention is to use the ivsk for establishing shared secrets for both encryption/decryption and for tagging.
It could be argued that if we were to use separate keypairs for tagging and for encryption, then it would enable finer control over the "viewability" of a user's activity. The ivsk could be shared with a trusted 3rd party to enable them to decrypt all incoming notes, messages, and events; and a distinct "tagging secret key" could be shared with a trusted 3rd party to give them a much more restricted ability to only determine which onchain logs are pertinent to the user, but not to decrypt those logs. The latter can be summarised as "I can see which logs are for you, but I can't see what they contain". This would enable the user to reveal to the 3rd party only select details about the logs with zero-knowledge proofs, whilst preventing the user from omitting any logs.
It's an interesting idea that came up when the keys scheme was being designed. It was preferred to have a recipient's address be the only piece of information that a sender needs in order to transact with them. The address is technically a public key, whose corresponding secret key is `ivsk + pre_address`. So to preserve the aim of only needing a user's address to transact, the `ivsk` naturally becomes the only secret key that should be used to establish shared secrets for both encryption and tagging.
This elegant requirement is slowly unravelling, however. Several use cases have been encountered where simply providing a sender with a recipient's address is insufficient, and actually items from the preimage of the address are needed:
- Patterns whereby a user A needs to execute an authwit function of another user B's account contract.
- In this case, the underlying private function ACIR -- and hence most of the preimage of the user B's address -- needs to be provided to the executor of the function (user A) so that they may execute the function, and because the kernels require this information.
- (There's outstanding research which could explore whether B could generate a _proof_ of the authwit function and associated kernel iteration and provide that to A. Currently, due to folding, B would leak witness data to A. Besides, requiring an entire witness from B still violates the unravelling aim of only requiring B's address).
- Patterns relating to the signing key, as introduced by this AZIP: if a user A wants to verify a signature from user B -- signed using B's signing secret key -- then A needs to be given the membership witness to prove existence of B's signing public key within B's address.
- Key rotation ideas would require a sender to read extra information (beyond a recipient's address) from a registry.
- Post-quantum keys (a future area of research) will undoubtedly require large amounts of data to be transferred between users. In this light, the notion of only sharing a small address between users is not sustainable.
Ok, so if we're beginning to realise that "only sharing a recipient's address" is not sustainable, then maybe we should entertain the continued usefulness of a separate tagging keypair. Let's explore common ways in which a shared secret can be established:
| Approach | Derivation |
|---|---|
| static-static | $S = \text{sk}_A \cdot \text{Pk}_B = \text{sk}_B \cdot \text{Pk}_A |
| ephemeral-static | $S = \text{ephemeral_sk} \cdot \text{Pk}_B = \text{sk}_B \cdot \text{Ephemeral_pk} |
| ephemeral-ephemeral | $S = \text{ephemeral_sk}_A \cdot \text{Ephemeral_pk}_B = \text{ephemeral_sk}_B \cdot \text{Ephemeral_pk}_A |
What happens if we give a trusted 3rd party access to the `ivsk` or `tsk` under these different scenarios. Can the trusted 3rd party actually derive all of a user's shared secrets and ultimately learn all plaintexts (for `ivsk`) / all pertinent logs (for `tsk`), solely given access to `ivsk` / `tsk` and public blockchain data?
The answer is no.
- In the case of static-static:
- Not all addresses are publicly known, so any shared secrets $S$ that are established offchain with non-public addresses cannot be derived by a trusted 3rd party.
- In the case of ephemeral-static:
- Only if the $\text{Ephemeral_pk}$ is broadcast to the public bulletin board (Aztec logs) will the 3rd party be able to derive the shared secret. Only use cases which adopt "non-interactive, constrained delivery" technically need to satisfy this. For all other use cases, there would not be sufficient public information for the trusted 3rd party to be able to derive all shared secrets -- be they tagging shared secets or encryption shared secrets.
- In the case of ephemeral-ephemeral:
- These shared secrets are completely detached from any user's secret keys, so a 3rd party's knowledge of `ivsk` or `tsk` is useless.
This isn't to say that the notion of "sharing viewing keys with 3rd parties" is dead. It just means that individual app developers who wish to offer this feature to users -- of guaranteeing that the 3rd party can see all pertinent information without omissions -- will need to think very carefully about how to constrain delivery of all relevant shared secret information to that 3rd party.
There are ways in which the two distinct "viewability" features -- of decrypting and of only being able to identify pertinent logs -- can be achieved without a distinct and canonical tagging keypair...
But, having written this out, perhaps before we remove the Tpk, we should explore "viewability" patterns in more detail. It could be that the Tpk is useful afterall, despite it not being useful for its original intended purpose. **Perhaps this AZIP should simply introduce the `(ssk, spk)` additively, instead of replacing the `(tsk, tpk)`.

Since the tagging key is unused by the protocol, by re-using it for this purpose we minimise the invasiveness of the change.
### Alternatives Considered

Comment thread
IlyasRidhuan marked this conversation as resolved.
#### Authwits

Aztec has "Account Abstraction", which means there are no protocol-specified keys to be used for authorising any transactions. Instead, users can write so-called "Account Contracts" which contain custom logic for tx authorisation.

Sometimes, an app function might wish to check with the owner of some state variable whether the owner has given permission to mutate the variable. An example is a `transfer_from` function of a token contract: since the `msg_sender` is not necessarily the _owner_ of the balance being decremented, the token contract seeks permission from the owner. Given the nature of "account abtraction", there is no canonical scheme for conveying "I give permission" or "I am authorising this action"; instead the user's account contract must be called, since the account contract can contain any abstract notion of "I am authorising this action". Hence, the `transfer_from` function can't contain inlined logic for authorisation; it must make a call to the owner's account contract and receive a `true` response.

A downside is that this process requires an extra kernel iteration to process that "authwit call" to the user's account contract, and kernel iterations cost proving time.

The notion of enshrining a dedicated "signing keypair", as introduced by this AZIP, can then simply be thought of as an optimisation to avoid a kernel iteration. In this sense, an alternative is to simply not implement this AZIP and instead accept the usage of authwit calls.

#### Migrations

[This forum comment](https://forum.aztec.network/t/request-for-grant-proposals-application-state-migration/8298/2) discusses the usefulness of an enshrined key -- instead of authwits -- in the case of state migrations.

Several teams are intending to abuse the tagging keypair for this purpose. This AZIP would repurpose the meaning of this key to align with this abuse.

From the forum comment:

> Why does Bob need another public key to prove who he is to the next rollup instance?
>
> Well, Aztec has account abstraction, which technically means there's no enshrined public key to represent Bob. Bob is represented by his account contract, which means the way Bob would prove "I am Bob" to a particular rollup instance is not "Here is a signature over some _canonical_ public key", but instead "A function of my account contract has successfully executed". The network doesn't care about the internals of that function: the function might actually validate a signature against some public key, but the network doesn't see that; it only recognises the successful execution of a function of Bob's account contract as evidence of "I am Bob".
>
> So if Bob wants to migrate his state from one ("old") rollup to another ("new") rollup, why can't he provide a proof of execution of a function of his old account contract to functions of the new rollup? Well, whether that is a safe approach would depend on _why_ a new rollup is being created. It's possible that the _reason_ for a new rollup is due to a bug in the old rollup (because the network is in its Alpha phase). If there's a bug in the proving system, for example, then Bob's old account contract might contain a bug. In that case, the new rollup should not trust proofs from Bob's old account contract. In that case, Bob's mechanism for proving "I am Bob" -- of furnishing a proof of successful execution of a function of his account contract -- is broken. App developers who wish to design ways for users to migrate state between rollups should account for this possibility.
>
> Hence, a new migration public key -- which cannot be corrupted by any bugs in the Alpha phase of Aztec -- is an attractive mechanism through which Bob could prove to functions of the new rollup "I was Bob on the old rollup, so please let me migrate Bob's state over to this new rollup. I have generated a new address to be the owner of that state on this new rollup".

There's an open question about whether the two use cases -- of signing as a form of acknowledgement, or of signing for the purpose of migrations -- have different threat models for how the secret keys should be stored. In the former case, theft of a signing secret key could lead to impersonation and a breakage of "non-repudiation"; in the latter case, theft of a migration secret key could lead to the attacker being able to establish a new (malicious) account contract on the new rollup and possibly migrate the user's state from the old rollup, effectively stealing from the user. In this light, it seems two new keypairs are required: a signing keypair and a dedicated migration keypair.
### Address Changes
While the address derivation is unchanged, whether a given account's address changes under this AZIP depends on whether the value stored at the renamed slot changes:
- A wallet that persists `tsk_m` can carry the same `Field` value forward as `spk_m`. The derived `public_keys_hash` is identical and the address is preserved.
- A wallet that re-derives the secret key from a seed sees its derived value change because of the domain-separator change.

It is RECOMMENDED that wallets re-derive the secret key and new address to not be reliant on legacy code paths.

### Why change the secret-key domain separator preimage
A new domain separator is used to preserve consistency across the protocol. Whilst this would ordinarily invalidate existing keys and addresses, this change MUST be shipped as part of a new Aztec rollup version.

An alternative of maintaining the original domain separator was explored but ultimately it was decided that permanently embedding the legacy notion of a `tsk` into the protocol was undesirable.

### Change to aztec-nr APIs
The following public API surface in aztec-nr MUST be updated:
- field name `tpk_m`, `TpkM`, `tagging_key` in any struct or accessor exposed to contract authors,
- comments and doc-strings referencing "tagging" tied to this key MUST be updated to "signing".

### Documentation Updates
All references to "tagging key" / "tagging public key" / "tagging secret key" tied to the renamed slot MUST be updated to the signing-key terminology. References to "tagging" in the context of note discovery (sender-`ivsk`-derived tags) are out of scope and MUST be preserved.

## Backwards Compatibility

This proposal is NOT backward compatible and represents a breaking change to the protocol. This AZIP MUST therefore be shipped as part of a new Aztec rollup version.

1. **Source-level breakage.** Any contract, indexer, or tool referencing `tpk`, `tsk`, `TpkM`, `DOM_SEP__TSK_M`, `KeyPrefix='t'`, or related identifiers will not compile or will fail schema validation.
2. **Key Derivation is invalidated** Any wallets that re-derive from a given master secret will no longer be able to generate the previous `tsk`. Additionally, re-derived addresses will be different.
3. **Event payload bytes unchanged.** Indexers that decode the `ContractInstancePublished` event by field offset remain compatible. Indexers that decode by field name MUST be updated.

## Copyright Waiver:
Copyright and related rights waived via [CC0](https://github.com/AztecProtocol/governance/blob/main/LICENSE).