Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
71 changes: 61 additions & 10 deletions l1-contracts/docs/registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,22 +152,73 @@ sequenceDiagram

### Validator Registration Process Explanation

1. The `Operator` calls `registerValidators()` on the `UniFiAVSManager`, providing the `podOwner` address and an array of BLS public key hashes for the validators to be registered.
There are two primary paths for validator registration and incorporates robust slashing mechanisms to maintain the integrity of the network. This document explores these paths and mechanisms in detail, highlighting key nuances such as registration delays and slashing conditions.

2. The `UniFiAVSManager` checks if the operator is registered with the AVS using the `AVSDirectory`.
## Validator Registration Path 1: Registering Validators with EigenPod

3. The `UniFiAVSManager` verifies that the operator has set an OperatorCommitment.
The `registerValidators` function is used for validators associated with an EigenPod. This method ensures that validators are actively managed and verified through the EigenLayers ecosystem.

4. For each BLS public key hash in provided:
- The `UniFiAVSManager` retrieves the validator information from the `EigenPod`.
- It checks if the validator is active in the EigenPod.
- It verifies that the validator is not already registered in the UniFi AVS.
- If all checks pass, it registers the validator, associating it with the operator and storing relevant information.
```solidity
function registerValidators(address podOwner, bytes32[] calldata blsPubKeyHashes) external;
```
Parameters:
- `podOwner`: The address of the pod owner.
- `blsPubKeyHashes`: An array of BLS public key hashes for the validators to be registered.

Example:

```solidity
bytes32[] memory pubKeyHashes = [0x1234..., 0x5678...];
uniFiAVSManager.registerValidators(podOwner, pubKeyHashes);
```
Process:
The function requires the caller to be the operator delegated to the pod owner, ensuring proper authorization. It also verifies that the operator is registered in the AVS (Active Validator Set) and checks that the validators are active and not already registered. Once these conditions are met, validators indefinitely.

## Validator Registration Path 2: Optimistic Registration for Independent Validators

This function is designed for independent validators, allowing for an optimistic registration process that prioritizes gas efficiency.

```solidity
function registerValidatorsOptimistically(ValidatorRegistrationParams[] calldata validators) external;
```

Parameters:
- `validators`: An array of `ValidatorRegistrationParams` structs, each containing the necessary data for registration.

Example:

```solidity
ValidatorRegistrationParams[] memory validators = new ValidatorRegistrationParams[](1);
validators[0] = ValidatorRegistrationParams({
blsPubKeyHash: 0x1234...,
index: 1,
salt: 123456,
expiry: block.timestamp + 1 days,
registrationSignature: BN254.G1Point({X: 0x5678..., Y: 0x9abc...})
});

uniFiAVSManager.registerValidatorsOptimistically(validators);
```

Process:
Validators are registered without immediate validation checks, assuming validity until proven otherwise. A key feature of this method is the registration delay, which introduces a window before validators become active. This delay allows time for potential slashing of invalid registrations, enhancing network security.

A critical feature of the optimistic registration process is the registration delay. This delay serves as a safeguard, allowing time for the network to identify and slash invalid validators before they become active. During this period, slashers can verify the validity of the registration and take action if necessary. This mechanism significantly enhances network security by preventing invalid validators from participating in the network.

5. The `UniFiAVSManager` updates the operator's validator count and resets their deregistration state if they had previously queued to deregister their operator.
To generate the `registrationSignature`, the validator needs to sign the BLS message hash using their BLS key containing the `operator`, `index`, `salt`, and `expiry`.

These checks will ensure that a validator can only be registered exactly once in the AVS, and that it can only be to the Operator whom the validator's podOwner is delegated to.
The mssage hash that needs to be signed can be fetched using the following view function:

```solidity
function blsMessageHash(address operator, uint256 salt, uint256 expiry, uint256 index)
public
view
returns (BN254.G1Point memory)
```
If you want to generate the hash yourself, you can use the following typehash:

`VALIDATOR_REGISTRATION_TYPEHASH =
keccak256("BN254ValidatorRegistration(address operator,bytes32 salt,uint256 expiry,uint64 index)");`

# Deregistering from UniFi AVS

Expand Down
88 changes: 82 additions & 6 deletions l1-contracts/docs/slashing.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,87 @@
# Slashing Mechanism

The slashing mechanism in UniFi AVS is designed to ensure the integrity of the pre-confirmation process. It consists of two main cases:
The slashing mechanism in UniFi AVS is designed to ensure the integrity of the pre-confirmation process. It consists of three main cases:

1. Safety Faults (Breaking Pre-confirmation Promises)
2. Liveness Faults (Missed Block Slashing)
1. Invalid Validator Registration
2. Safety Faults (Breaking Pre-confirmation Promises)
3. Liveness Faults (Missed Block Slashing)

## Safety Faults
## Invalid Validator Registration
To maintain the integrity of the network, the UniFiAVSManager contract includes several mechanisms to slash operators who register invalid validators. Slashing acts as a deterrent against fraudulent or incorrect registrations.

### Slashing Validators with Invalid Registration Signatures

```solidity
function slashValidatorsWithInvalidSignature(ValidatorRegistrationSlashingParams[] calldata validators) external;
```
Parameters:
- `validators`: An array of `ValidatorRegistrationSlashingParams` structs, each containing the necessary data for slashing.

Example:
```solidity
ValidatorRegistrationSlashingParams[] memory validators = new ValidatorRegistrationSlashingParams[](1);
validators[0] = ValidatorRegistrationSlashingParams({
pubkeyG1: BN254.G1Point({X: 0x1234..., Y: 0x5678...}),
pubkeyG2: BN254.G2Point({X: [0x9abc..., 0xdef0...], Y: [0x1234..., 0x5678...]}),
registrationSignature: BN254.G1Point({X: 0x9abc..., Y: 0xdef0...}),
expiry: block.timestamp + 1 days,
salt: 123456,
index: 1 // index of the validator
});

uniFiAVSManager.slashValidatorsWithInvalidSignature(validators);
```

Mechanism:
It checks the validity of the registration signature using BLS signature verification. If the signature is found to be invalid, the validator is slashed, and the operator is penalized. This mechanism maintains the authenticity of registrations, ensuring that only legitimate validators are part of the network.

### Slashing Validators with Invalid Index

```solidity
function slashValidatorsWithInvalidIndex(BeaconChainHelperLib.InclusionProof[] calldata proofs) external;
```
Parameters:
- `proofs`: An array of `BeaconChainHelperLib.InclusionProof` structs, each containing the necessary data for slashing.

Example:

```solidity
BeaconChainHelperLib.InclusionProof[] memory proofs = new BeaconChainHelperLib.InclusionProof[](1);
proofs[0] = BeaconChainHelperLib.InclusionProof({
validator: [0x1234...],
validatorIndex: 1,
// Additional proof data...
});

uniFiAVSManager.slashValidatorsWithInvalidIndex(proofs);
```

Mechanism:
This function verifies the validator's index against the provided proof. If the index does not match, the validator is slashed. This mechanism prevents the misuse of validator indices, ensuring that each index is unique and correctly assigned.

### Slashing Validators with Invalid Public Key

```solidity
function slashValidatorsWithInvalidPubkey(BeaconChainHelperLib.InclusionProof[] calldata proofs) external;
```
Parameters:
- `proofs`: An array of `BeaconChainHelperLib.InclusionProof` structs, each containing the necessary data for slashing.

Example:
```solidity
BeaconChainHelperLib.InclusionProof[] memory proofs = new BeaconChainHelperLib.InclusionProof[](1);
proofs[0] = BeaconChainHelperLib.InclusionProof({
validator: [0x1234...],
// Additional proof data...
});

uniFiAVSManager.slashValidatorsWithInvalidPubkey(proofs);
```

Mechanism:
Similar to the previous mechanisms, this function verifies the validator's public key against the provided proof. If the public key is found to be invalid, the validator is slashed. This mechanism ensures the integrity of the validator's public key, preventing unauthorized or incorrect registrations.

## Safety Faults (Not Implemented)

Safety faults occur when a validator breaks their pre-conf promise. This category encompasses a larger design space compared to Liveness faults, including:

Expand All @@ -21,7 +97,7 @@ b) Execution Pre-conf Violations:

The larger design space for Safety faults allows for more complex and nuanced slashing conditions, which can be expanded and refined as the pre-confirmation ecosystem evolves.

## Liveness Faults
## Liveness Faults (Not Implemented)

Liveness faults occur when:

Expand All @@ -31,7 +107,7 @@ Liveness faults occur when:

This mechanism ensures that validators cannot abuse the pre-confirmation system by making promises they don't intend to keep due to inactivity.

## Slashing Process
## Slashing Process for Liveness Faults and Safety Faults

The slashing process involves two key components:

Expand Down
25 changes: 22 additions & 3 deletions l1-contracts/script/DeployEverything.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { DeployUniFiAVSManager } from "script/DeployUniFiAVSManager.s.sol";
import { SetupAccess } from "script/SetupAccess.s.sol";
import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol";
import { AVSDeployment } from "script/DeploymentStructs.sol";
import { console } from "forge-std/console.sol";

import { UniFiAVSDisputeManager } from "../src/UniFiAVSDisputeManager.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
/**
* @title Deploy all protocol contracts
* @author Puffer Finance
* @notice Deploys all contracts for the AVS and sets up the access control
* @dev Example on how to run the script
* forge script script/DeployEverything.s.sol:DeployEverything --rpc-url=$RPC_URL --sig 'run()' --broadcast
*/

contract DeployEverything is BaseScript {
address DAO;

Expand All @@ -28,16 +29,32 @@ contract DeployEverything is BaseScript {

vm.startBroadcast(_deployerPrivateKey);
AccessManager accessManager = new AccessManager(_broadcaster);

// Deploy DisputeManager
UniFiAVSDisputeManager disputeManagerImplementation = new UniFiAVSDisputeManager();
address disputeManager = address(
new ERC1967Proxy{ salt: bytes32("UniFiAVSDisputeManager") }(
address(disputeManagerImplementation),
abi.encodeCall(UniFiAVSDisputeManager.initialize, (address(accessManager)))
)
);

vm.stopBroadcast();

// 1. Deploy AVSManager
(address avsManagerImplementation, address avsManagerProxy) = new DeployUniFiAVSManager().run(
address(accessManager), eigenPodManager, eigenDelegationManager, avsDirectory, initialDeregistrationDelay
address(accessManager),
eigenPodManager,
eigenDelegationManager,
avsDirectory,
initialDeregistrationDelay,
disputeManager
);

deployment.avsManagerImplementation = avsManagerImplementation;
deployment.avsManagerProxy = avsManagerProxy;
deployment.accessManager = address(accessManager);
deployment.disputeManagerProxy = disputeManager;

// `anvil` in the terminal
if (_localAnvil) {
Expand Down Expand Up @@ -65,6 +82,8 @@ contract DeployEverything is BaseScript {
vm.serializeAddress(obj, "avsManagerImplementation", deployment.avsManagerImplementation);
vm.serializeAddress(obj, "avsManagerProxy", deployment.avsManagerProxy);
vm.serializeAddress(obj, "accessManager", deployment.accessManager);
vm.serializeAddress(obj, "disputeManagerImplementation", deployment.disputeManagerImplementation);
vm.serializeAddress(obj, "disputeManagerProxy", deployment.disputeManagerProxy);
vm.serializeAddress(obj, "dao", DAO);

string memory finalJson = vm.serializeString(obj, "", "");
Expand Down
10 changes: 7 additions & 3 deletions l1-contracts/script/DeployUniFiAVSManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { IEigenPodManager } from "eigenlayer/interfaces/IEigenPodManager.sol";
import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol";
import { IAVSDirectory } from "eigenlayer/interfaces/IAVSDirectory.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { console } from "forge-std/console.sol";
import { IUniFiAVSDisputeManager } from "../src/interfaces/IUniFiAVSDisputeManager.sol";

contract DeployUniFiAVSManager is BaseScript {
UniFiAVSManager public uniFiAVSManagerProxy;
Expand All @@ -17,11 +17,15 @@ contract DeployUniFiAVSManager is BaseScript {
address eigenPodManager,
address eigenDelegationManager,
address avsDirectory,
uint64 initialDeregistrationDelay
uint64 initialDeregistrationDelay,
address disputeManager
) public returns (address, address) {
vm.startBroadcast(_deployerPrivateKey);
UniFiAVSManager uniFiAVSManagerImplementation = new UniFiAVSManager(
IEigenPodManager(eigenPodManager), IDelegationManager(eigenDelegationManager), IAVSDirectory(avsDirectory)
IEigenPodManager(eigenPodManager),
IDelegationManager(eigenDelegationManager),
IAVSDirectory(avsDirectory),
IUniFiAVSDisputeManager(disputeManager)
);

uniFiAVSManagerProxy = UniFiAVSManager(
Expand Down
19 changes: 16 additions & 3 deletions l1-contracts/script/DeployUniFiAVSManagerWithMocks.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol
import { IAVSDirectory } from "eigenlayer/interfaces/IAVSDirectory.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol";
import "forge-std/console.sol";

import "../test/mocks/MockEigenPodManager.sol";
import "../test/mocks/MockDelegationManager.sol";
import "../test/mocks/MockAVSDirectory.sol";
import "../src/UniFiAVSDisputeManager.sol";
import { IUniFiAVSDisputeManager } from "../src/interfaces/IUniFiAVSDisputeManager.sol";
import { console } from "forge-std/console.sol";

contract DeployUniFiAVSManagerWithMocks is BaseScript {
UniFiAVSManager public uniFiAVSManagerProxy;
Expand All @@ -21,16 +22,27 @@ contract DeployUniFiAVSManagerWithMocks is BaseScript {
address eigenDelegationManager;
address avsDirectory;
uint64 initialDeregistrationDelay = 0;
address disputeManager;

function run() public broadcast returns (address, address) {
eigenPodManager = address(new MockEigenPodManager());
eigenDelegationManager = address(new MockDelegationManager());
avsDirectory = address(new MockAVSDirectory());

accessManager = new AccessManager(_broadcaster);
UniFiAVSDisputeManager disputeManagerImplementation = new UniFiAVSDisputeManager();
disputeManager = address(
new ERC1967Proxy{ salt: bytes32("UniFiAVSDisputeManager") }(
address(disputeManagerImplementation),
abi.encodeCall(UniFiAVSDisputeManager.initialize, (address(accessManager)))
)
);

UniFiAVSManager uniFiAVSManagerImplementation = new UniFiAVSManager(
IEigenPodManager(eigenPodManager), IDelegationManager(eigenDelegationManager), IAVSDirectory(avsDirectory)
IEigenPodManager(eigenPodManager),
IDelegationManager(eigenDelegationManager),
IAVSDirectory(avsDirectory),
IUniFiAVSDisputeManager(disputeManager)
);

uniFiAVSManagerProxy = UniFiAVSManager(
Expand All @@ -49,6 +61,7 @@ contract DeployUniFiAVSManagerWithMocks is BaseScript {
console.log("eigenPodManager mock:", address(eigenPodManager));
console.log("eigenDelegationManager mock:", address(eigenDelegationManager));
console.log("avsDirectory mock:", address(avsDirectory));
console.log("disputeManager mock:", address(disputeManager));

return (address(uniFiAVSManagerImplementation), address(uniFiAVSManagerProxy));
}
Expand Down
2 changes: 2 additions & 0 deletions l1-contracts/script/DeploymentStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ struct AVSDeployment {
address accessManager;
address timelock;
address dao;
address disputeManagerProxy;
address disputeManagerImplementation;
}
3 changes: 3 additions & 0 deletions l1-contracts/script/Roles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ uint64 constant ROLE_ID_AVS_COORDINATOR_ALLOWLISTER = 5;

// Lockbox role for ETH Mainnet
uint64 constant ROLE_ID_LOCKBOX = 7;

// Role for UniFiAVSManager
uint64 constant ROLE_ID_UNIFI_AVS_MANAGER = 30;
Loading