From eed10066c6ac010a61e699cab0968f5e7aa98e64 Mon Sep 17 00:00:00 2001 From: Kamal Nayan Date: Sat, 13 Jul 2024 12:30:55 +0530 Subject: [PATCH 1/4] avs slashing trap init --- .gitmodules | 3 ++ avs-slashing/.github/workflows/test.yml | 45 +++++++++++++++++ avs-slashing/.gitignore | 14 ++++++ avs-slashing/README.md | 66 +++++++++++++++++++++++++ avs-slashing/foundry.toml | 6 +++ avs-slashing/script/Counter.s.sol | 19 +++++++ 6 files changed, 153 insertions(+) create mode 100644 avs-slashing/.github/workflows/test.yml create mode 100644 avs-slashing/.gitignore create mode 100644 avs-slashing/README.md create mode 100644 avs-slashing/foundry.toml create mode 100644 avs-slashing/script/Counter.s.sol diff --git a/.gitmodules b/.gitmodules index c0faed2..30863c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/avs-slashing/.github/workflows/test.yml b/avs-slashing/.github/workflows/test.yml new file mode 100644 index 0000000..762a296 --- /dev/null +++ b/avs-slashing/.github/workflows/test.yml @@ -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 diff --git a/avs-slashing/.gitignore b/avs-slashing/.gitignore new file mode 100644 index 0000000..85198aa --- /dev/null +++ b/avs-slashing/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/avs-slashing/README.md b/avs-slashing/README.md new file mode 100644 index 0000000..9265b45 --- /dev/null +++ b/avs-slashing/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/avs-slashing/foundry.toml b/avs-slashing/foundry.toml new file mode 100644 index 0000000..25b918f --- /dev/null +++ b/avs-slashing/foundry.toml @@ -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 diff --git a/avs-slashing/script/Counter.s.sol b/avs-slashing/script/Counter.s.sol new file mode 100644 index 0000000..cdc1fe9 --- /dev/null +++ b/avs-slashing/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} From 3855b7d869d8ec730556ee9506ecf9541865878e Mon Sep 17 00:00:00 2001 From: Kamal Nayan Date: Mon, 15 Jul 2024 12:30:55 +0530 Subject: [PATCH 2/4] write the contract for the avs trap --- avs-slashing/src/AVSSlashingTrap.sol | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 avs-slashing/src/AVSSlashingTrap.sol diff --git a/avs-slashing/src/AVSSlashingTrap.sol b/avs-slashing/src/AVSSlashingTrap.sol new file mode 100644 index 0000000..d640291 --- /dev/null +++ b/avs-slashing/src/AVSSlashingTrap.sol @@ -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 + } +} \ No newline at end of file From d897a634a3510a3fb5b5940255912d0389f32279 Mon Sep 17 00:00:00 2001 From: Kamal Nayan Date: Mon, 15 Jul 2024 15:30:55 +0530 Subject: [PATCH 3/4] write the test file --- avs-slashing/test/AVSSlashingTrapTest.t.sol | 65 +++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 avs-slashing/test/AVSSlashingTrapTest.t.sol diff --git a/avs-slashing/test/AVSSlashingTrapTest.t.sol b/avs-slashing/test/AVSSlashingTrapTest.t.sol new file mode 100644 index 0000000..71b9e2e --- /dev/null +++ b/avs-slashing/test/AVSSlashingTrapTest.t.sol @@ -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"); + } +} \ No newline at end of file From d0954c5e0dfb92b66b46fd6a65788265a89d24ac Mon Sep 17 00:00:00 2001 From: Kamal Nayan Date: Mon, 15 Jul 2024 18:30:55 +0530 Subject: [PATCH 4/4] all test cases are passing --- avs-slashing/README.md | 123 +++++++++++++++++++----------- avs-slashing/script/Counter.s.sol | 19 ----- 2 files changed, 80 insertions(+), 62 deletions(-) delete mode 100644 avs-slashing/script/Counter.s.sol diff --git a/avs-slashing/README.md b/avs-slashing/README.md index 9265b45..b1dbd5e 100644 --- a/avs-slashing/README.md +++ b/avs-slashing/README.md @@ -1,66 +1,103 @@ -## Foundry +## AVS Slash Detection -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +This Trap aims to create a Drosera trap for detecting slashing incidents in the context of Active Validator Services (AVS) using EigenLayer, -Foundry consists of: +I will first provide a brief of the mechanisms behind slashing and the implications of validator behavior in the Ethereum ecosystem. -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. +## Understanding AVS Slashing -## Documentation +### What is AVS Slashing? -https://book.getfoundry.sh/ +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: -## Usage +- **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. -### Build +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. -```shell -$ forge build -``` +### Importance of Slashing Detection -### Test +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. -```shell -$ forge test -``` +## Designing the Drosera Trap -### Format +The Drosera trap is a smart contract that monitors validator behavior and detects slashing incidents based on the following criteria: -```shell -$ forge fmt -``` +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. -### Gas Snapshots +## Explanation of the Code -```shell -$ forge snapshot -``` +### State Variables -### Anvil +- **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. -```shell -$ anvil -``` +### Events -### Deploy +- **SlashingDetected**: Emitted when a slashing incident is detected. +- **ValidatorUpdated**: Emitted when a validator's data is updated. -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` +### Functions -### Cast +- **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. -```shell -$ cast -``` +- **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. -### Help +### Contract Definition -```shell -$ forge --help -$ anvil --help -$ cast --help +- `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. \ No newline at end of file diff --git a/avs-slashing/script/Counter.s.sol b/avs-slashing/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/avs-slashing/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -}