Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ERC-6538: Move to Review #259

Merged
merged 26 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a9d514d
Add Security Considerations, Deployment Method and update contract
garyghayrat Dec 14, 2023
8f9af33
Add full source code and update Security Considerations section
garyghayrat Dec 21, 2023
5889352
Update Security Considerations section
garyghayrat Dec 21, 2023
770badd
Update Security Considerations wording
garyghayrat Jan 4, 2024
6cd92a3
Update to latest contract code
garyghayrat Feb 6, 2024
ea5e861
Fix typo
garyghayrat Feb 16, 2024
65e1592
Add `NonceIncremented` event to contract
garyghayrat Feb 20, 2024
b0f6162
Add Security Considerations, Deployment Method and update contract
garyghayrat Dec 14, 2023
efbff77
Add full source code and update Security Considerations section
garyghayrat Dec 21, 2023
ac4e694
Update Security Considerations section
garyghayrat Dec 21, 2023
27850b2
Update Security Considerations wording
garyghayrat Jan 4, 2024
f4551a4
Update to latest contract code
garyghayrat Feb 6, 2024
19da343
Add `NonceIncremented` event to contract
garyghayrat Feb 20, 2024
9ff5275
Update license to CC0-1.0
garyghayrat Feb 23, 2024
ca7bee7
Update description, add salt and contract address, & fix nits
garyghayrat Feb 27, 2024
0d6a177
Fix typo
garyghayrat Feb 27, 2024
d6bf877
Remove opinionated recommendation
garyghayrat Mar 4, 2024
87be2e6
Add contract and interface to the asset folder
garyghayrat Mar 5, 2024
f091066
Fix typo
garyghayrat Mar 5, 2024
ea074ff
Format and fix typo
garyghayrat Mar 5, 2024
8ec839f
Remove non-relative links
garyghayrat Mar 5, 2024
db9a011
Add EIP links and update asset links
garyghayrat Mar 5, 2024
d7479bd
Remove EIP non-relative links
garyghayrat Mar 5, 2024
4ce3a3e
Add EIP-712,1271 in `required` section
garyghayrat Mar 8, 2024
7b8e215
Remove typo
garyghayrat Mar 11, 2024
5d60571
Update contract with hashed `stealthMetaAddress`
garyghayrat Mar 22, 2024
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
275 changes: 230 additions & 45 deletions ERCS/erc-6538.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
---
eip: 6538
title: Stealth Meta-Address Registry
description: A registry to map addresses to stealth meta-addresses
author: Matt Solomon (@mds1), Toni Wahrstätter (@nerolation), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin)
description: A canonical contract for entities to register stealth meta-addresses directly or through a third party using signatures.
author: Matt Solomon (@mds1), Toni Wahrstätter (@nerolation), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin), Gary Ghayrat (@garyghayrat)
discussions-to: https://ethereum-magicians.org/t/stealth-meta-address-registry/12888
status: Stagnant
status: Review
type: Standards Track
category: ERC
created: 2023-01-24
garyghayrat marked this conversation as resolved.
Show resolved Hide resolved
requires: 712, 1271, 5564
---

## Abstract

This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md).
This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md). An entity may register their stealth meta-address directly. A third party can also register on behalf of an entity using a valid [EIP-712](./eip-712.md) or [EIP-1271](./eip-1271.md) signature. Once registered, the stealth meta-address for the entity can be retrieved by any smart contract or user. One can use the stealth-meta address with `generateStealthAddress` specified in [ERC-5564](./eip-5564.md) to send assets to the generated stealth address without revealing the entity's address.

## Motivation

Expand All @@ -22,70 +23,255 @@ The standardization of stealth address generation holds the potential to greatly

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

This contract defines an `ERC5564Registry` that stores the stealth meta-address for entities. These entities may be identified by an address, ENS name, or other identifier. This MUST be a singleton contract, with one instance per chain.
This contract defines an `ERC6538Registry` that stores the stealth meta-address for entities. These entities may be identified by an address, ENS name, or other identifier. This MUST be a singleton contract, with one instance per chain.

The contract is specified below. A one byte integer is used to identify the stealth address scheme. This integer is used to differentiate between different stealth address schemes. A mapping from the scheme ID to it's specification is maintained at [this](../assets/eip-5564/scheme_ids.md) location.
The contract is specified below. A one byte integer is used to identify the stealth address scheme. This integer is used to differentiate between different stealth address schemes. This ERC outlines schemeId `1` as the SECP256k1 curve cryptographic scheme with view tags, as specified in [ERC-5564](./eip-5564.md).

```solidity
pragma solidity ^0.8.17;
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.23;

/// @notice Registry to map an address or other identifier to its stealth meta-address.
contract ERC5564Registry {
/// @dev Emitted when a registrant updates their stealth meta-address.
/// @notice `ERC6538Registry` contract to map accounts to their stealth meta-address. See
/// [ERC-6538](https://eips.ethereum.org/EIPS/eip-6538) to learn more.
contract ERC6538Registry {
/// @notice Emitted when an invalid signature is provided to `registerKeysOnBehalf`.
error ERC6538Registry__InvalidSignature();

/// @notice Next nonce expected from `user` to use when signing for `registerKeysOnBehalf`.
/// @dev `registrant` may be a standard 160-bit address or any other identifier.
/// @dev `schemeId` is an integer identifier for the stealth address scheme.
mapping(address registrant => mapping(uint256 schemeId => bytes)) public stealthMetaAddressOf;

/// @notice A nonce used to ensure a signature can only be used once.
/// @dev `registrant` is the user address.
/// @dev `nonce` will be incremented after each valid `registerKeysOnBehalf` call.
mapping(address registrant => uint256) public nonceOf;

/// @notice The EIP-712 type hash used in `registerKeysOnBehalf`.
bytes32 public constant ERC6538REGISTRY_ENTRY_TYPE_HASH =
keccak256("Erc6538RegistryEntry(uint256 schemeId,bytes stealthMetaAddress,uint256 nonce)");

/// @notice The chain ID where this contract is initially deployed.
uint256 internal immutable INITIAL_CHAIN_ID;

/// @notice The domain separator used in this contract.
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

/// @notice Emitted when a registrant updates their stealth meta-address.
/// @param registrant The account that registered the stealth meta-address.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address.
/// [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) bases the format for stealth
/// meta-addresses on [ERC-3770](https://eips.ethereum.org/EIPS/eip-3770) and specifies them as:
/// st:<shortName>:0x<spendingPubKey>:<viewingPubKey>
/// The chain (`shortName`) is implicit based on the chain the `ERC6538Registry` is deployed on,
/// therefore this `stealthMetaAddress` is just the compressed `spendingPubKey` and
/// `viewingPubKey` concatenated.
event StealthMetaAddressSet(
bytes indexed registrant, uint256 indexed scheme, bytes stealthMetaAddress
address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress
);

/// @notice Maps a registrant's identifier to the scheme to the stealth meta-address.
/// @dev Registrant may be a 160 bit address or other recipient identifier, such as an ENS name.
/// @dev Scheme is an integer identifier for the stealth address scheme.
/// @dev MUST return zero if a registrant has not registered keys for the given inputs.
mapping(bytes => mapping(uint256 => bytes)) public stealthMetaAddressOf;
/// @notice Emitted when a registrant increments their nonce.
/// @param registrant The account that incremented the nonce.
/// @param newNonce The new nonce value.
event NonceIncremented(address indexed registrant, uint256 newNonce);

constructor() {
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}

/// @notice Sets the caller's stealth meta-address for the given stealth address scheme.
/// @param scheme An integer identifier for the stealth address scheme.
/// @notice Sets the caller's stealth meta-address for the given scheme ID.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address to register.
function registerKeys(uint256 scheme, bytes memory stealthMetaAddress) external {
stealthMetaAddressOf[abi.encode(msg.sender)][scheme] = stealthMetaAddress;
function registerKeys(uint256 schemeId, bytes calldata stealthMetaAddress) external {
stealthMetaAddressOf[msg.sender][schemeId] = stealthMetaAddress;
emit StealthMetaAddressSet(msg.sender, schemeId, stealthMetaAddress);
}

/// @notice Sets the `registrant`s stealth meta-address for the given scheme.
/// @param registrant Recipient identifier, such as an ENS name.
/// @param scheme An integer identifier for the stealth address scheme.
/// @notice Sets the `registrant`'s stealth meta-address for the given scheme ID.
/// @param registrant Address of the registrant.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param signature A signature from the `registrant` authorizing the registration.
/// @param stealthMetaAddress The stealth meta-address to register.
/// @dev MUST support both EOA signatures and EIP-1271 signatures.
/// @dev MUST revert if the signature is invalid.
/// @dev Supports both EOA signatures and EIP-1271 signatures.
/// @dev Reverts if the signature is invalid.
function registerKeysOnBehalf(
address registrant,
uint256 scheme,
uint256 schemeId,
bytes memory signature,
bytes memory stealthMetaAddress
bytes calldata stealthMetaAddress
) external {
// TODO If registrant has no code, spit signature into r, s, and v and call `ecrecover`.
// TODO If registrant has code, call `isValidSignature` on the registrant.
bytes32 dataHash;
address recoveredAddress;

unchecked {
dataHash = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
ERC6538REGISTRY_ENTRY_TYPE_HASH,
schemeId,
keccak256(stealthMetaAddress),
nonceOf[registrant]++
)
)
)
);
}

if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
recoveredAddress = ecrecover(dataHash, v, r, s);
}

if (
(
(recoveredAddress == address(0) || recoveredAddress != registrant)
&& (
IERC1271(registrant).isValidSignature(dataHash, signature)
!= IERC1271.isValidSignature.selector
)
)
) revert ERC6538Registry__InvalidSignature();

stealthMetaAddressOf[registrant][schemeId] = stealthMetaAddress;
emit StealthMetaAddressSet(registrant, schemeId, stealthMetaAddress);
}

/// @notice Increments the nonce of the sender to invalidate existing signatures.
function incrementNonce() external {
unchecked {
nonceOf[msg.sender]++;
}
emit NonceIncremented(msg.sender, nonceOf[msg.sender]);
}

/// @notice Sets the `registrant`s stealth meta-address for the given scheme.
/// @param registrant Recipient identifier, such as an ENS name.
/// @param scheme An integer identifier for the stealth address scheme.
/// @notice Returns the domain separator used in this contract.
/// @dev The domain separator is re-computed if there's a chain fork.
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
}

/// @notice Computes the domain separator for this contract.
function _computeDomainSeparator() internal view returns (bytes32) {
return keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256("ERC6538Registry"),
keccak256("1.0"),
block.chainid,
address(this)
)
);
}
}

/// @notice Interface of the ERC1271 standard signature validation method for contracts as defined
/// in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
interface IERC1271 {
/// @notice Should return whether the signature provided is valid for the provided data
/// @param hash Hash of the data to be signed
/// @param signature Signature byte array associated with _data
function isValidSignature(bytes32 hash, bytes memory signature)
external
view
returns (bytes4 magicValue);
}

```

The interface for this contract is defined below:

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.23;

/// @dev Interface for calling the `ERC6538Registry` contract to map accounts to their stealth
/// meta-address. See [ERC-6538](https://eips.ethereum.org/EIPS/eip-6538) to learn more.
interface IERC6538Registry {
/// @notice Emitted when an invalid signature is provided to `registerKeysOnBehalf`.
error ERC6538Registry__InvalidSignature();

/// @dev Emitted when a registrant updates their stealth meta-address.
/// @param registrant The account that registered the stealth meta-address.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address.
/// [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) bases the format for stealth
/// meta-addresses on [ERC-3770](https://eips.ethereum.org/EIPS/eip-3770) and specifies them as:
/// st:<shortName>:0x<spendingPubKey>:<viewingPubKey>
/// The chain (`shortName`) is implicit based on the chain the `ERC6538Registry` is deployed on,
/// therefore this `stealthMetaAddress` is just the `spendingPubKey` and `viewingPubKey`
/// concatenated.
event StealthMetaAddressSet(
address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress
);

/// @notice Emitted when a registrant increments their nonce.
/// @param registrant The account that incremented the nonce.
/// @param newNonce The new nonce value.
event NonceIncremented(address indexed registrant, uint256 newNonce);

/// @notice Sets the caller's stealth meta-address for the given scheme ID.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address to register.
function registerKeys(uint256 schemeId, bytes calldata stealthMetaAddress) external;

/// @notice Sets the `registrant`'s stealth meta-address for the given scheme ID.
/// @param registrant Address of the registrant.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param signature A signature from the `registrant` authorizing the registration.
/// @param stealthMetaAddress The stealth meta-address to register.
/// @dev MUST support both EOA signatures and EIP-1271 signatures.
/// @dev MUST revert if the signature is invalid.
/// @dev Supports both EOA signatures and EIP-1271 signatures.
/// @dev Reverts if the signature is invalid.
function registerKeysOnBehalf(
bytes memory registrant,
uint256 scheme,
address registrant,
uint256 schemeId,
bytes memory signature,
bytes memory stealthMetaAddress
) external {
// TODO How to best generically support any registrant identifier / name
// system without e.g. hardcoding support just for ENS?
}
bytes calldata stealthMetaAddress
) external;

/// @notice Increments the nonce of the sender to invalidate existing signatures.
function incrementNonce() external;

/// @notice Returns the domain separator used in this contract.
function DOMAIN_SEPARATOR() external view returns (bytes32);

/// @notice Returns the stealth meta-address for the given `registrant` and `schemeId`.
function stealthMetaAddressOf(address registrant, uint256 schemeId)
external
view
returns (bytes memory);

/// @notice Returns the EIP-712 type hash used in `registerKeysOnBehalf`.
function ERC6538REGISTRY_ENTRY_TYPE_HASH() external view returns (bytes32);

/// @notice Returns the nonce of the given `registrant`.
function nonceOf(address registrant) external view returns (uint256);
}

```

Deployment is done using the keyless deployment method commonly known as Nick’s method, TODO continue describing this and include transaction data, can base it off the format/description used in [ERC-1820](./eip-1820.md) and [ERC-2470](./eip-2470.md).
### Deployment Method

The `ERC6538Registry` contract is deployed at `0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538` using `CREATE2` via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of `0x7cac4e512b1768c627c9e711c7a013f1ad0766ef5125c59fb7161dade58da078`.

## Rationale

Expand All @@ -105,13 +291,12 @@ This EIP is fully backward compatible.

## Reference Implementation

You can find an implementation of this standard above.
You can find an implementation of the `ERC6538Registry` contract [here](../assets/eip-6538/contracts/ERC6538Registry.sol) and the interface `IERC6538Registry.sol` [here](../assets/eip-6538/contracts/interfaces/IERC6538Registry.sol).

## Security Considerations

TODO
In the event of a compromised private key, the registrant should promptly un-register from the stealth key registry to prevent loss of future funds sent to the compromised account.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).

Loading
Loading