Skip to content

Commit

Permalink
feat: contracts v3.1 (#402)
Browse files Browse the repository at this point in the history
* feat: extract Registration

* feat(KeyRegistry): enforce max keys per fid

* feat: skip validation in migration

* feat: extract KeyManager

* chore: update deployment + tests

* chore: docs updates

* fix: move IdManager owner arg

* chore: run Halmos on shared runner

* chore: update gas snapshot

* chore: grant PR write permission to coverage

* feat: remove keys/storage from trustedBatchRegister

* feat: remove TrustedCaller from KeyRegistry

* chore: comment fixes

* chore: review cleanup

* chore: remove unused imports

* fix: move validator interaction

* fix: coverage misses

* feat: refactor to pause guardians

* fix: run Halmos on macos-latest

Co-authored-by: Daejun Park <daejunpark@gmail.com>

* chore: bundler natspec

* chore: Guardian/TrustedCaller symtests

* feat: constrain decreasing maxKeysPerFid

* chore: rename managers gateways

* feat: remove payment from KeyGateway

* feat: add external useNonce()

* fix: deadline validation

* chore: remove unused Pausable imports

* feat: add optional extraStorage to IdGateway

* fix: add previous recovery to recovery sig

* refactor: extract abstract Migration helper

* chore: extract interfaces

* refactor: move all errors and events to interfaces

* feat: add migration helpers to IdRegistry

* feat: remove trustedCaller from IdGateway

* feat: remove trustedCaller from Bundler

* chore: remove references to trustedCaller

* feat: migrate with default recovery

* feat: freezable gateways

* fix: e2e and container tests

* chore: update docs

* chore: update Mermaid chart

* feat: deploy Migration in paused state

* refactor: pass explicit migrator

* fix: update symtests

* chore: add MIGRATOR_ADDRESS env var

* fix: expected VERSION in CI

* fix: missing tests for setMigrator

* feat: enumerable keys

* refactor: separate libraries, omit from coverage

* fix: exclude precompiles from fuzzed addrs

* chore: update natspec

* feat: add paged lookup

* chore: update protocol versions

* chore: move IdRegistry gas tests to IdGateway

* fix: version in CI check

* fix: use generic storage for KeyRegistry symtest

* fix: temporarily remove .gas-snapshot

* fix: regenerate gas snapshot

* fix: update symtests

* chore: add paged key tests

* fix: remove unused variable

* fix: remove src/.gas-snapshot

* fix: update version in docs URLs

* fix: settable StorageRegistry on IdGateway

* refactor: s/migration/onlyMigrator

* chore: change security contact

* feat: transfer and change recovery

* chore: add natspec

* fix: remove redundant line

* fix: state mutability

* feat: track removed keys

* fix: symtest updates

* fix: add KeyRegistry symtest harness

* chore: update upgrade script

* refactor: calldata keys

* chore: add post-checks to deploy script

* chore: add upgrade deploy parameter

* chore: add upgrade salts

* chore: comment cleanup

* chore: update deployment params

---------

Co-authored-by: Daejun Park <daejunpark@gmail.com>
  • Loading branch information
horsefacts and daejunpark committed Nov 6, 2023
1 parent 74784dc commit 0451f3f
Show file tree
Hide file tree
Showing 75 changed files with 7,537 additions and 2,908 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ RECOVERY_PROXY_CREATE2_SALT=0x6d2b70e39c6bc63763098e336323591eb77cd0c6110eaaca06

# Deployer address.
DEPLOYER=0x6D2b70e39C6bc63763098e336323591eb77Cd0C6

# Default migrator address.
MIGRATOR_ADDRESS=0x2D93c2F74b2C4697f9ea85D0450148AA45D4D5a2
4 changes: 4 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ RECOVERY_PROXY_CREATE2_SALT=0x00000000000000000000000000000000000000000000000000
# Deployer address.
# Default address is Anvil test account 1
DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

# Migrator address.
# Default address is Anvil test account 1
MIGRATOR_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
3 changes: 3 additions & 0 deletions .env.prod
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ RECOVERY_PROXY_CREATE2_SALT=0x6d2b70e39c6bc63763098e336323591eb77cd0c6110eaaca06

# Deployer address.
DEPLOYER=0x6D2b70e39C6bc63763098e336323591eb77Cd0C6

# Migrator address.
MIGRATOR_ADDRESS=0x2D93c2F74b2C4697f9ea85D0450148AA45D4D5a2
55 changes: 55 additions & 0 deletions .env.upgrade
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Price feed addresses
STORAGE_RENT_PRICE_FEED_ADDRESS=0x13e3ee699d1909e989722e753853ae30b17e08c5
STORAGE_RENT_UPTIME_FEED_ADDRESS=0x371EAD81c9102C9BF4874A9075FFFf170F2Ee389

# Storage rent params
STORAGE_RENT_ROLE_ADMIN_ADDRESS=0x53c6dA835c777AD11159198FBe11f95E5eE6B692
STORAGE_RENT_VAULT_ADDRESS=0x53c6dA835c777AD11159198FBe11f95E5eE6B692
STORAGE_RENT_ADMIN_ADDRESS=0xD84E32224A249A575A09672Da9cb58C381C4837a
STORAGE_RENT_OPERATOR_ADDRESS=0x0000000000000000000000000000000000000000
STORAGE_RENT_TREASURER_ADDRESS=0x0000000000000000000000000000000000000000

# ID registry params
ID_REGISTRY_OWNER_ADDRESS=0x53c6dA835c777AD11159198FBe11f95E5eE6B692

# Key registry params
KEY_REGISTRY_OWNER_ADDRESS=0x53c6dA835c777AD11159198FBe11f95E5eE6B692

# Metadata validator params
METADATA_VALIDATOR_OWNER_ADDRESS=0x53c6dA835c777AD11159198FBe11f95E5eE6B692

# Bundler params
BUNDLER_TRUSTED_CALLER_ADDRESS=
BUNDLER_OWNER_ADDRESS=

# Recovery proxy params
RECOVERY_PROXY_OWNER_ADDRESS=0xFFE52568Fb0E7038Ef289677288BB704E5c9E82e

# Fname resolver params.
FNAME_RESOLVER_SERVER_URL=https://fnames.farcaster.xyz/ccip/{sender}/{data}.json
FNAME_RESOLVER_SIGNER_ADDRESS=0xBc5274eFc266311015793d89E9B591fa46294741
FNAME_RESOLVER_OWNER_ADDRESS=0x138356f24c7A16BE48978dE277a468F6C16A19a5

# RPC endpoints for OP testnet and mainnet.
L1_MAINNET_RPC_URL=
L2_MAINNET_RPC_URL=

# Salts
STORAGE_RENT_CREATE2_SALT=0x0000000000000000000000000000000000000000000000000000000000000000
ID_REGISTRY_CREATE2_SALT=0x299707e127cc77de01b9fd968bc0ff475f3c6342d7872c397cd084029fbf64dc
ID_GATEWAY_CREATE2_SALT=0x299707e127cc77de01b9fd968bc0ff475f3c6342a1b2a1fd9db0df01f0373563
KEY_REGISTRY_CREATE2_SALT=0x299707e127cc77de01b9fd968bc0ff475f3c6342aee9be2412b02b01eb294554
KEY_GATEWAY_CREATE2_SALT=0x299707e127cc77de01b9fd968bc0ff475f3c6342229ded5ec3c3bd02e574f7be
SIGNED_KEY_REQUEST_VALIDATOR_CREATE2_SALT=0x0000000000000000000000000000000000000000000000000000000000000000
BUNDLER_CREATE2_SALT=0x299707e127cc77de01b9fd968bc0ff475f3c6342e9da01d98917640342e02a5c
RECOVERY_PROXY_CREATE2_SALT=0x299707e127cc77de01b9fd968bc0ff475f3c63421958ea987b94fd038a490454

# Deployed contracts
STORAGE_RENT_ADDRESS=0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D
SIGNED_KEY_REQUEST_VALIDATOR_ADDRESS=0x00000000FC700472606ED4fA22623Acf62c60553

# Deployer address.
DEPLOYER=0x299707E127CC77DE01b9Fd968Bc0ff475f3C6342

# Migrator address.
MIGRATOR_ADDRESS=0x2D93c2F74b2C4697f9ea85D0450148AA45D4D5a2
11 changes: 4 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
BundleRegistryGasUsageTest:testGasRegisterWithSig() (gas: 834748)
BundleRegistryGasUsageTest:testGasTrustedBatchRegister() (gas: 7081493)
BundleRegistryGasUsageTest:testGasTrustedRegister() (gas: 917155)
IdRegistryGasUsageTest:testGasRegister() (gas: 734577)
IdRegistryGasUsageTest:testGasRegisterForAndRecover() (gas: 1741741)
IdRegistryGasUsageTest:testGasRegisterFromTrustedCaller() (gas: 807023)
BundleRegistryGasUsageTest:testGasRegisterWithSig() (gas: 1118748)
IdGatewayGasUsageTest:testGasRegister() (gas: 1402640)
IdGatewayGasUsageTest:testGasRegisterForAndRecover() (gas: 1973946)
StorageRegistryGasUsageTest:testGasBatchCredit() (gas: 173053)
StorageRegistryGasUsageTest:testGasBatchRent() (gas: 270570)
StorageRegistryGasUsageTest:testGasBatchRent() (gas: 270579)
StorageRegistryGasUsageTest:testGasContinuousCredit() (gas: 166530)
StorageRegistryGasUsageTest:testGasCredit() (gas: 81890)
StorageRegistryGasUsageTest:testGasRent() (gas: 163250)
12 changes: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ env:
L1_MAINNET_RPC_URL: ${{ secrets.L1_MAINNET_RPC_URL }}
L2_MAINNET_RPC_URL: ${{ secrets.L2_MAINNET_RPC_URL }}


jobs:
build-image:
timeout-minutes: 5
Expand Down Expand Up @@ -87,7 +86,7 @@ jobs:

- name: Confirm Bundler contract was deployed
shell: bash
run: '[ $(cast call $BUNDLER_CONTRACT_ADDRESS "owner()") = 0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266 ]'
run: '[ $(cast call $BUNDLER_CONTRACT_ADDRESS "VERSION()") = 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a323032332e31312e313500000000000000000000000000000000000000000000 ]'

test:
strategy:
Expand Down Expand Up @@ -120,7 +119,7 @@ jobs:
run: forge snapshot --check --match-contract Gas

halmos:
runs-on: macos-latest
runs-on: ${{ vars.BUILDJET_DISABLED == 'true' && 'macos-latest' || 'buildjet-16vcpu-ubuntu-2204' }}
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -137,9 +136,12 @@ jobs:
run: pip install halmos

- name: Run halmos
run: halmos --error-unknown --test-parallel --solver-parallel --solver-timeout-assertion 0
run: halmos --error-unknown --test-parallel --solver-parallel --storage-layout=generic --solver-timeout-assertion 0

coverage:
permissions:
contents: read
pull-requests: write
runs-on: ${{ vars.BUILDJET_DISABLED == 'true' && 'ubuntu-latest' || 'buildjet-2vcpu-ubuntu-2204' }}
steps:
- uses: actions/checkout@v3
Expand All @@ -158,7 +160,7 @@ jobs:
- name: Filter directories
run: |
sudo apt update && sudo apt install -y lcov
lcov --remove lcov.info 'test/*' 'script/*' --output-file lcov.info --rc lcov_branch_coverage=1
lcov --remove lcov.info 'test/*' 'script/*' 'src/libraries/*' --output-file lcov.info --rc lcov_branch_coverage=1
# Post a detailed coverage report as a comment and deletes previous comments on each push.
- name: Post coverage report
Expand Down
6 changes: 0 additions & 6 deletions .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
"func-visibility": ["error", { "ignoreConstructors": true }],
"max-line-length": ["error", 120],
"not-rely-on-time": "off",
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"reason-string": ["warn", { "maxLength": 64 }]
}
}
125 changes: 94 additions & 31 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ graph TD
end
subgraph ETHL2["Ethereum L2: OP Mainnet"]
RP(Recovery Proxy) --> IR(Id Registry)
BN(Bundler) --> IR & SR(Storage Registry) & KR(Key Registry)
KR --> IR
BN(Bundler) --> IG(Id Gateway)
BN --> KG(Key gateway)
IG --> SR(Storage Registry)
IG --> IR(Id Registry)
KG --> KR(Key Registry)
KR --> SKRV(Signed Key Request Validator)
KR --> IR
RP(RecoveryProxy) --> IG
end
Expand All @@ -40,11 +45,13 @@ graph TD

1. [L2 Contracts](#1-l2-contracts)
1. [Id Registry](#11-id-registry)
2. [Storage Registry](#12-storage-registry)
3. [Key Registry](#13-key-registry)
4. [Validators](#14-validators)
5. [Bundler](#15-bundler)
6. [Recovery Proxy](#16-recovery-proxy)
2. [Id Gateway](#12-id-manager)
3. [Storage Registry](#13-storage-registry)
4. [Key Registry](#14-key-registry)
5. [Key Gateway](#15-key-manager)
6. [Validators](#16-validators)
7. [Bundler](#17-bundler)
8. [Recovery Proxy](#18-recovery-proxy)
2. [L1 Contracts](#2-l1-contracts)
1. [Fname Resolver](#21-fname-resolver)
3. [Off-chain Systems](#3-off-chain-systems)
Expand All @@ -70,51 +77,77 @@ IdRegistry lets any Ethereum address claim a unique Farcaster ID or `fid`. Fids

1. owner is not malicious.

### Administration

The owner can pause and unpause the contract, which pauses registration, transfer, and recovery. The owner has one time use permissions to migrate data during the migration phase.

### Migration

When deployed, the IdRegistry starts in the Seedable state, where only the trusted caller can register fids. Identities from previous versions of the contracts can be registered to their addresses by the owner. Once complete, the owner can move it to the Registrable state, where anyone can register fids. This state change cannot be reversed.
The IdRegistry is deployed in a trusted state where keys may not be registered by anyone except the owner. The owner will populate the KeyRegistry with existing state by using bulk operations. Once complete, the owner will call `migrate()` to set a migration timestamp and emit an event. Hubs watch for the `Migrated` event and 24 hours after it is emitted, they cut over to this contract as the source of truth.

### Upgradeability

The IdRegistry contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. In such cases:

1. A new IdRegistry contract is deployed.
2. The current IdRegistry contract is paused.
3. The new IdRegistry is seeded with all the registered fids in the old contract.
4. The KeyRegistry is updated to point to the new IdRegistry.
5. A new Bundler contract is deployed, pointing to the correct contracts.

## 1.2. Id Gateway

The IdManager is responsible for fid registration. While IdRegistry defines the rules of fid ownership, transfers, and
recovery, the manager is responsible for the the actual registration logic. To prevent spamming fid registrations, the
IdManager requires callers to rent 1 [storage unit](#13-storage-registry) at fid registration time.

### Invariants

1. In untrusted mode, callers must rent 1 storage unit at fid registration time.
2. The contract can only transition to untrusted mode once, and never back to trusted mode.

### Assumptions

1. owner is not malicious.

### Administration

The owner can pause and unpause the contract, which pauses registration, transfer, and recovery.

### State Machine

An fid can exist in three states:
An fid can exist in two states:

- `seedable` - the fid has never been issued and can be registered by the trusted caller
- `registrable` - the fid has never been issued and can be registered by anyone
- `registered` - the fid has been issued to an address

```mermaid
stateDiagram-v2
direction LR
seedable --> registrable: disable trusted only
seedable --> registered: trusted register
registrable --> registered: register
registered --> registered: transfer, recover
```

The fid state transitions when users take specific actions:

- `register` - register a new fid from any address
- `trusted register` - register a new fid from the trusted caller
- `disable trusted only` - allow registration from any sender
- `transfer` - move an fid to a new custody address
- `recover` - recover (move) an fid to a new custody address

### Upgradeability

The IdRegistry contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. In such cases:
The IdManager contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. Since
the IdManager depends on storage, we expect to update this contract in the future if the storage system changes.

1. A new IdRegistry contract is deployed in the seedable state.
2. The current IdRegistry contract is paused.
3. The new IdRegistry is seeded with all the registered fids in the old contract.
4. The KeyRegistry is updated to point to the new IdRegistry.
5. A new Bundler contract is deployed, pointing to the correct contracts.
6. The new IdRegistry is moved to the registrable state where anyone can register an fid.
In such cases:

1. A new IdManager contract is deployed in the seedable state.
2. The IdRegistry is updated to point to the new IdManager.
3. The old IdManager is paused.
4. A new Bundler contract is deployed, pointing to the correct contracts.
5. The new IdManager is moved to the registrable state where anyone can register an fid.

## 1.2. Storage Registry
## 1.3. Storage Registry

The StorageRegistry contract lets anyone rent units of storage space on Farcaster Hubs for a given fid. Payment must be made in Ethereum to acquire storage for a year. Acquiring storage emits an event that is read off-chain by the Farcaster Hubs, which allocate space to the user. The contract will deprecate itself one year after deployment, and we expect to launch a new contract with updated logic. For more details, see [FIP-6](https://github.com/farcasterxyz/protocol/discussions/98).

Expand Down Expand Up @@ -150,7 +183,7 @@ An `operator` role can credit storage to fids without the payment of rent. This

A `treasurer` role can move funds from the contract to a pre-defined `vault` address, but cannot change this destination. Only the `owner` may change the vault address to a new destination. The `treasurer` may also refresh the oracle price.

An `owner` role can modify many parameters including the total supply of storage units, the price of rent, the duration for which exchange prices are valid and the deprecation timestamp. The owner may also pause and unpause the contract, disabling/enabling rentals and credits.
An `owner` role can modify many parameters including the total supply of storage units, the price of rent, the duration for which exchange prices are valid and the deprecation timestamp. The owner may also pause and unpause the contract, disabling/enabling rentals and credits.

### Upgradeability

Expand All @@ -164,7 +197,7 @@ The StorageRegistry contract may need to be upgraded in case a bug is discovered

## 1.3. Key Registry

The Key Registry contract lets addresses with an fid add or remove public keys. Keys added onchain are tracked by Hubs and can be used to sign Farcaster messages. The same key can be added by different fids and can exist in different states. Keys contain a key type that indicates how they should be interpreted and used. During registration, metadata can also be emitted to provide additional context about the key. Keys contain a metadata type indicating how this metadata should be validated and interpreted. The Key Registry validates metadata at registration time and rejects keys with invalid metadata.
The Key Registry contract lets addresses with an fid register or remove public keys. Keys added onchain are tracked by Hubs and can be used to sign Farcaster messages. The same key can be added by different fids and can exist in different states. Keys contain a key type that indicates how they should be interpreted and used. During registration, metadata can also be emitted to provide additional context about the key. Keys contain a metadata type indicating how this metadata should be validated and interpreted. The Key Registry validates metadata at registration time and rejects keys with invalid metadata.

### Key Types

Expand All @@ -181,6 +214,7 @@ Key types may have multiple associated metadata types, indicating how their asso
3. Removal: A key can only move to the removed state if it was previously in the added state.
4. Reset: A key can only move to the null state if it was previously in the added state, the contract hasn't been migrated, and the action was performed by the owner.
5. Events: Event invariants are specified in comments above each event.
6. Limits: A new key may not be added if its addition would exceed the keys per fid limit.

### Assumptions

Expand All @@ -189,7 +223,7 @@ Key types may have multiple associated metadata types, indicating how their asso

### Migration

The KeyRegistry is deployed in the trusted state where keys may not be registered by anyone except the owner. The owner will populate the KeyRegistry with existing state by using bulk operations. Once complete, the owner will call `migrateKeys()` to set a migration timestamp and emit an event. Hubs watch for the `Migrated` event and 24 hours after it is emitted, they cut over to this contract as the source of truth.
The KeyRegistry is deployed in the trusted state where keys may not be registered by anyone except the owner. The owner will populate the KeyRegistry with existing state by using bulk operations. Once complete, the owner will call `migrate()` to set a migration timestamp and emit an event. Hubs watch for the `Migrated` event and 24 hours after it is emitted, they cut over to this contract as the source of truth.

### State Machine

Expand Down Expand Up @@ -231,29 +265,58 @@ The KeyRegistry contract may need to be upgraded in case a bug is discovered or
4. A new Bundler contract is deployed, pointing to the correct contracts.
5. The contract is set to untrusted state where anyone can register keys.

## 1.4 Validators
## 1.4. Key Gateway

The Key Gateway is the user-facing contract responsible for adding new keys to the Key Registry. While IdRegistry defines the rules of key addition and deletion, the Key Gateway is responsible for the the actual addition logic.

### Invariants

1. Fee: A key may only be added

### Assumptions

1. The KeyRegistry contract is functional.
1. The StorageRegistry contract is functional.
1. owner is not malicious.

### Administration

The Key Gateway owner may can pause and unpause the contract, disabling/enabling adding keys to the Key Registry.

### Upgradeability

The KeyManager contract may need to be upgraded in case a bug is discovered or the logic needs to be changed.

In such cases:

1. A new KeyManager contract is deployed.
2. The KeyRegistry is updated to point to the new KeyManager.
3. The old KeyManager is paused.
4. A new Bundler contract is deployed, pointing to the correct contracts.

## 1.5 Validators

Validators are single purpose contracts that implement a simple interface to validate key metadata. At registration time, the Key Registry looks up the associated validator by key type and metadata type, and calls it to validate the format of provided metadata. This makes the key registry extensible to future key types and metadata formats.

### 1.4.1 Signed Key Request Validator
### 1.5.1 Signed Key Request Validator

The only validator today is the Signed Key Request Validator, which validates that EdDSA key metadata is a "signed key request." A signed key request represents a third party request to add a public key associated with an fid. Requesting parties must own an fid in order to identify their key requests, and sign a message over their fid and the public key in order to authenticate their request. This allows third party applications requesting signer keys to identify themselves to users, and users to validate the authenticity of signer requests before approving them onchain.

#### Administration

An `owner` can update the address of the Id Registry contract.

## 1.5. Bundler
## 1.6. Bundler

The Bundler contract lets a caller register an fid, rent storage units and register a key in a single transaction to save gas. It is a simple wrapper around contract methods and contains little logic beyond tracking contract addresses, collecting parameters and invoking the appropriate functions.

## 1.6 Recovery Proxy
## 1.7 Recovery Proxy

The Recovery Proxy is an immutable proxy contract that allows the recovery execution logic to change without changing the recovery address associated with an fid. A client or recovery service operator can deploy a recovery proxy and use it as the recovery address for fids. For example, the Warpcast client uses a recovery proxy owned by a 2/3 multisig as the default recovery address for new accounts.

#### Administration

A recovery proxy can change its `owner`, and may be owned by an EOA, multisig, or smart contract.
A recovery proxy can change its `owner`, and may be owned by an EOA, multisig, or smart contract. The `owner` of the recovery proxy can change the configured `IdRegistry` address.

# 2. L1 Contracts

Expand Down
Loading

0 comments on commit 0451f3f

Please sign in to comment.