Skip to content
Open
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@
[submodule "bridges/nomad/lib/forge-std"]
path = bridges/nomad/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "hello_foundry/lib/forge-std"]
path = hello_foundry/lib/forge-std
url = https://github.com/foundry-rs/forge-std
45 changes: 45 additions & 0 deletions avs-slashing/.github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

on:
push:
pull_request:
workflow_dispatch:

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Show Forge version
run: |
forge --version

- name: Run Forge fmt
run: |
forge fmt --check
id: fmt

- name: Run Forge build
run: |
forge build --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
14 changes: 14 additions & 0 deletions avs-slashing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
103 changes: 103 additions & 0 deletions avs-slashing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
## AVS Slash Detection

This Trap aims to create a Drosera trap for detecting slashing incidents in the context of Active Validator Services (AVS) using EigenLayer,

I will first provide a brief of the mechanisms behind slashing and the implications of validator behavior in the Ethereum ecosystem.

## Understanding AVS Slashing

### What is AVS Slashing?

Slashing is a mechanism used in Proof of Stake (PoS) networks, including those using EigenLayer, to penalize validators for malicious behavior or significant negligence. This includes actions such as:

- **Double signing**: A validator signs two different blocks for the same slot.
- **Prolonged inactivity**: A validator fails to participate in consensus over a specified period.

The purpose of slashing is to maintain network security and reliability by deterring validators from acting maliciously or negligently. EigenLayer allows validators to restake their assets across multiple services, thereby increasing the risk of slashing if they misbehave in any of the services they validate.

### Importance of Slashing Detection

Detecting slashing incidents is critical for maintaining trust and security within the network. A Drosera trap can automate the detection of these incidents, allowing for timely responses to potential threats.

## Designing the Drosera Trap

The Drosera trap is a smart contract that monitors validator behavior and detects slashing incidents based on the following criteria:

1. **Signature Count**: Track the number of signatures a validator has made in a given block.
2. **Activity Monitoring**: Monitor the last active block of a validator to check for prolonged inactivity.
3. **Incident Reporting**: Emit events when slashing is detected.

## Explanation of the Code

### State Variables

- **validatorSignatures**: A mapping that tracks the number of signatures made by each validator.
- **lastActiveBlock**: A mapping that records the last block number in which a validator was active.
- **THRESHOLD_BLOCKS**: A constant that defines the number of blocks a validator can be inactive before being flagged for slashing.
- **MAX_SIGNATURES**: A constant that sets the maximum number of signatures a validator can have in a single block without being flagged.

### Events

- **SlashingDetected**: Emitted when a slashing incident is detected.
- **ValidatorUpdated**: Emitted when a validator's data is updated.

### Functions

- **isValid**: Checks whether a validator's behavior is valid based on their signature count and activity. Returns `false` if an incident is detected.

- **updateValidatorData**: Updates the validator's signature count and last active block. It also checks for slashing incidents after updating the data.

- **resetValidatorData**: Resets the validator's data, which can be useful for reinitializing state after an incident.

## Future improvements Scope

This smart contract serves as a Drosera trap for detecting AVS slashing incidents by monitoring validator activity and signature behavior. It can be integrated with additional incident response mechanisms, such as alerting systems or automated actions based on detected slashing incidents, ensuring the security and reliability of the AVS ecosystem.

### Testing File

All test cases are passing
![image](https://github.com/user-attachments/assets/2e9c1a27-573f-43bb-8b8c-8abf447bf370)

To create a test file for the `AVSSlashingTrap` smart contract using Foundry, I have written a series of unit tests that validate the contract's functionality. These tests cover various scenarios, including updating validator data, checking for slashing incidents, and ensuring that the contract behaves as expected under different conditions.

## Explanation of the Test File

### Imports

- The test file imports the `forge-std/Test.sol` library for testing utilities and the `AVSSlashingTrap` contract.

### Contract Definition

- `AVSSlashingTrapTest` is defined as the test contract, inheriting from `Test`.

### Setup Function

- The `setUp` function initializes a new instance of the `AVSSlashingTrap` contract before each test.

### Test Cases

1. **testInitialValidatorState**: Verifies that the initial state for a new validator is zero signatures and zero last active block.

2. **testUpdateValidatorData**: Tests the `updateValidatorData` function to ensure it correctly updates the validator's signature count and last active block.

3. **testValidValidator**: Checks that a validator with valid data is recognized as valid.

4. **testSlashingDetection**:
- Tests that exceeding the maximum allowed signatures results in a slashing incident.
- Simulates inactivity by rolling the block number forward and checks if the validator is detected as slashed.

5. **testResetValidatorData**: Tests the `resetValidatorData` function to ensure it resets the validator's state correctly.

6. **testMultipleValidators**: Tests the functionality with multiple validators, ensuring that slashing detection for one validator does not affect the others.

## Running the Tests

To run the tests, navigate to the project directory in your terminal and execute:

```bash
forge test
```

This command will compile the contracts and run the tests, providing output on the success or failure of each test case.

This test suite ensures that the `AVSSlashingTrap` contract behaves as expected under various scenarios, providing confidence in its functionality and reliability.
6 changes: 6 additions & 0 deletions avs-slashing/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
48 changes: 48 additions & 0 deletions avs-slashing/src/AVSSlashingTrap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract AVSSlashingTrap {
// State variables to store validator data
mapping(address => uint256) public validatorSignatures;
mapping(address => uint256) public lastActiveBlock;
uint256 public constant THRESHOLD_BLOCKS = 10; // Number of blocks to check for inactivity
uint256 public constant MAX_SIGNATURES = 1; // Maximum allowed signatures in a block

event SlashingDetected(address indexed validator);
event ValidatorUpdated(address indexed validator, uint256 signatures);

// Function to check for slashing incidents
function isValid(address validator) public view returns (bool) {
// Check if the validator has signed more than allowed
if (validatorSignatures[validator] > MAX_SIGNATURES) {
return false; // Incident detected
}

// Check if the validator has been inactive for too long
if (block.number - lastActiveBlock[validator] > THRESHOLD_BLOCKS) {
return false; // Incident detected
}

return true; // No incident
}

// Function to update validator data
function updateValidatorData(address validator, uint256 signatures) public {
validatorSignatures[validator] += signatures;
lastActiveBlock[validator] = block.number;

emit ValidatorUpdated(validator, validatorSignatures[validator]);

// Check for slashing after updating data
if (!isValid(validator)) {
emit SlashingDetected(validator);
// Additional logic for incident response can be implemented here
}
}

// Function to reset validator data (optional)
function resetValidatorData(address validator) public {
validatorSignatures[validator] = 0;
lastActiveBlock[validator] = block.number; // Reset to current block
}
}
65 changes: 65 additions & 0 deletions avs-slashing/test/AVSSlashingTrapTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../src/AVSSlashingTrap.sol";

contract AVSSlashingTrapTest is Test {
AVSSlashingTrap public trap;

address validator1 = address(0x1);
address validator2 = address(0x2);

function setUp() public {
trap = new AVSSlashingTrap();
}

function testInitialValidatorState() public view {
assertEq(trap.validatorSignatures(validator1), 0);
assertEq(trap.lastActiveBlock(validator1), 0);
}

function testUpdateValidatorData() public {
trap.updateValidatorData(validator1, 1);
assertEq(trap.validatorSignatures(validator1), 1);
assertEq(trap.lastActiveBlock(validator1), block.number);
}

function testValidValidator() public {
trap.updateValidatorData(validator1, 1);
bool isValid = trap.isValid(validator1);
assertTrue(isValid, "Validator should be valid");
}

function testSlashingDetection() public {
trap.updateValidatorData(validator1, 2); // Exceeding max signatures
bool isValid = trap.isValid(validator1);
assertFalse(isValid, "Validator should be detected as slashing");

// Simulate inactivity
vm.roll(block.number + 11); // Move forward 11 blocks
isValid = trap.isValid(validator1);
assertFalse(isValid, "Validator should be detected as slashing due to inactivity");
}

function testResetValidatorData() public {
trap.updateValidatorData(validator1, 1);
trap.resetValidatorData(validator1);
assertEq(trap.validatorSignatures(validator1), 0);
assertEq(trap.lastActiveBlock(validator1), block.number);
}

function testMultipleValidators() public {
trap.updateValidatorData(validator1, 1);
trap.updateValidatorData(validator2, 1);

// Check both validators
assertTrue(trap.isValid(validator1), "Validator 1 should be valid");
assertTrue(trap.isValid(validator2), "Validator 2 should be valid");

// Trigger slashing for validator 1
trap.updateValidatorData(validator1, 2); // Exceeding max signatures
assertFalse(trap.isValid(validator1), "Validator 1 should be detected as slashing");
assertTrue(trap.isValid(validator2), "Validator 2 should still be valid");
}
}