Skip to content

Commit

Permalink
Merge pull request #21 from symbioticfi/bls
Browse files Browse the repository at this point in the history
bls
  • Loading branch information
1kresh authored Jan 27, 2025
2 parents 26ebedd + 5396c39 commit a65b247
Show file tree
Hide file tree
Showing 18 changed files with 2,136 additions and 573 deletions.
4 changes: 2 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ quote_style = "double"
tab_width = 4

[fuzz]
runs = 4096
max_test_rejects = 262144
runs = 64
max_test_rejects = 1262144

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
2 changes: 1 addition & 1 deletion lib/core
Submodule core updated 213 files
167 changes: 167 additions & 0 deletions src/examples/sqrt-task-network/BLSSqrtTaskMiddleware.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {IVault} from "@symbiotic/interfaces/vault/IVault.sol";
import {IBaseDelegator} from "@symbiotic/interfaces/delegator/IBaseDelegator.sol";
import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

import {BaseMiddleware} from "../../middleware/BaseMiddleware.sol";
import {SharedVaults} from "../../extensions/SharedVaults.sol";
import {SelfRegisterOperators} from "../../extensions/operators/SelfRegisterOperators.sol";

import {OwnableAccessManager} from "../../extensions/managers/access/OwnableAccessManager.sol";
import {KeyManagerBLS} from "../../extensions/managers/keys/KeyManagerBLS.sol";
import {TimestampCapture} from "../../extensions/managers/capture-timestamps/TimestampCapture.sol";
import {EqualStakePower} from "../../extensions/managers/stake-powers/EqualStakePower.sol";

contract BLSSqrtTaskMiddleware is
SharedVaults,
SelfRegisterOperators,
KeyManagerBLS,
OwnableAccessManager,
TimestampCapture,
EqualStakePower
{
using Subnetwork for address;
using Math for uint256;

error InvalidHints();
error TaskCompleted();

event CreateTask(uint256 indexed taskIndex);
event CompleteTask(uint256 indexed taskIndex, bool isValidAnswer);

struct Task {
uint48 captureTimestamp;
uint48 deadlineTimestamp;
uint256 value;
bool completed;
}

uint48 public constant TASK_DURATION = 1 days;
Task[] public tasks;

constructor(
address network,
uint48 slashingWindow,
address operatorRegistry,
address vaultRegistry,
address operatorNetOptin,
address reader,
address owner
) {
initialize(network, slashingWindow, vaultRegistry, operatorRegistry, operatorNetOptin, reader, owner);
}

function initialize(
address network,
uint48 slashingWindow,
address vaultRegistry,
address operatorRegistry,
address operatorNetOptin,
address reader,
address owner
) internal initializer {
__BaseMiddleware_init(network, slashingWindow, vaultRegistry, operatorRegistry, operatorNetOptin, reader);
__OwnableAccessManager_init(owner);
__SelfRegisterOperators_init("BLS Sqrt Task", 0);
}

function createTask(uint256 value, address operator) external returns (uint256 taskIndex) {
taskIndex = tasks.length;
tasks.push(
Task({
captureTimestamp: getCaptureTimestamp(),
deadlineTimestamp: getCaptureTimestamp() + TASK_DURATION,
value: value,
completed: false
})
);

emit CreateTask(taskIndex);
}

function completeTask(
uint256 taskIndex,
uint256 answer,
bytes calldata signature
) external returns (bool isValidAnswer) {
if (!_verify(taskIndex, answer, signature)) {
// revert InvalidAnswer();
}

tasks[taskIndex].completed = true;
emit CompleteTask(taskIndex, true);
}

function _verify(uint256 taskIndex, uint256 answer, bytes calldata signature) private view returns (bool) {
if (tasks[taskIndex].completed) {
revert TaskCompleted();
}
// _verifySignature(taskIndex, answer, signature);
return _verifyAnswer(taskIndex, answer);
}

function _verifyAnswer(uint256 taskIndex, uint256 answer) private view returns (bool) {
uint256 value = tasks[taskIndex].value;
uint256 square = answer ** 2;
if (square == value) {
return true;
}

if (square < value) {
uint256 difference = value - square;
uint256 nextSquare = (answer + 1) ** 2;
uint256 nextDifference = nextSquare > value ? nextSquare - value : value - nextSquare;
if (difference <= nextDifference) {
return true;
}
} else {
uint256 difference = square - value;
uint256 prevSquare = (answer - 1) ** 2;
uint256 prevDifference = prevSquare > value ? prevSquare - value : value - prevSquare;
if (difference <= prevDifference) {
return true;
}
}

return false;
}

// function _slash(uint256 taskIndex, bytes[] calldata stakeHints, bytes[] calldata slashHints) private {
// Task storage task = tasks[taskIndex];
// address[] memory vaults = _activeVaultsAt(task.captureTimestamp, task.operator);
// uint256 vaultsLength = vaults.length;

// if (stakeHints.length != slashHints.length || stakeHints.length != vaultsLength) {
// revert InvalidHints();
// }

// bytes32 subnetwork = _NETWORK().subnetwork(0);
// for (uint256 i; i < vaultsLength; ++i) {
// address vault = vaults[i];
// uint256 slashAmount = IBaseDelegator(IVault(vault).delegator()).stakeAt(
// subnetwork, task.operator, task.captureTimestamp, stakeHints[i]
// );

// if (slashAmount == 0) {
// continue;
// }

// _slashVault(task.captureTimestamp, vault, subnetwork, task.operator, slashAmount, slashHints[i]);
// }
// }

// function executeSlash(
// uint48 epochStart,
// address vault,
// bytes32 subnetwork,
// address operator,
// uint256 amount,
// bytes memory hints
// ) external checkAccess {
// _slashVault(epochStart, vault, subnetwork, operator, amount, hints);
// }
}
2 changes: 0 additions & 2 deletions src/examples/sqrt-task-network/SqrtTaskMiddleware.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/Signa

import {BaseMiddleware} from "../../middleware/BaseMiddleware.sol";
import {SharedVaults} from "../../extensions/SharedVaults.sol";
import {Operators} from "../../extensions/operators/Operators.sol";

import {OwnableAccessManager} from "../../extensions/managers/access/OwnableAccessManager.sol";
import {NoKeyManager} from "../../extensions/managers/keys/NoKeyManager.sol";
Expand All @@ -21,7 +20,6 @@ import {EqualStakePower} from "../../extensions/managers/stake-powers/EqualStake
// WARING: this is a simple example, it's not secure and should not be used in production
contract SqrtTaskMiddleware is
SharedVaults,
Operators,
NoKeyManager,
EIP712,
OwnableAccessManager,
Expand Down
65 changes: 32 additions & 33 deletions src/extensions/managers/keys/KeyManager256.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@ import {PauseableEnumerableSet} from "../../../libraries/PauseableEnumerableSet.
abstract contract KeyManager256 is KeyManager {
uint64 public constant KeyManager256_VERSION = 1;

using PauseableEnumerableSet for PauseableEnumerableSet.Bytes32Set;
using PauseableEnumerableSet for PauseableEnumerableSet.Status;

error DuplicateKey();
error MaxDisabledKeysReached();

bytes32 private constant ZERO_BYTES32 = bytes32(0);
uint256 private constant MAX_DISABLED_KEYS = 1;
error PreviousKeySlashable();

struct KeyManager256Storage {
/// @notice Mapping from operator addresses to their keys
mapping(address => PauseableEnumerableSet.Bytes32Set) _keys;
/// @notice Mapping from keys to operator addresses
mapping(bytes32 => address) _keyToOperator;
mapping(address => bytes32) _key;
mapping(address => bytes32) _prevKey;
mapping(bytes32 => PauseableEnumerableSet.InnerAddress) _keyData;
}

// keccak256(abi.encode(uint256(keccak256("symbiotic.storage.KeyManager256")) - 1)) & ~bytes32(uint256(0xff))
Expand All @@ -47,7 +43,7 @@ abstract contract KeyManager256 is KeyManager {
bytes memory key
) public view override returns (address) {
KeyManager256Storage storage $ = _getKeyManager256Storage();
return $._keyToOperator[abi.decode(key, (bytes32))];
return $._keyData[abi.decode(key, (bytes32))].value;
}

/**
Expand All @@ -59,11 +55,16 @@ abstract contract KeyManager256 is KeyManager {
address operator
) public view override returns (bytes memory) {
KeyManager256Storage storage $ = _getKeyManager256Storage();
bytes32[] memory active = $._keys[operator].getActive(getCaptureTimestamp());
if (active.length == 0) {
return abi.encode(ZERO_BYTES32);
uint48 timestamp = getCaptureTimestamp();
bytes32 key = $._key[operator];
if (key != bytes32(0) && $._keyData[key].status.wasActiveAt(timestamp)) {
return abi.encode(key);
}
key = $._prevKey[operator];
if (key != bytes32(0) && $._keyData[key].status.wasActiveAt(timestamp)) {
return abi.encode(key);
}
return abi.encode(active[0]);
return abi.encode(bytes32(0));
}

/**
Expand All @@ -75,7 +76,7 @@ abstract contract KeyManager256 is KeyManager {
function keyWasActiveAt(uint48 timestamp, bytes memory key_) public view override returns (bool) {
KeyManager256Storage storage $ = _getKeyManager256Storage();
bytes32 key = abi.decode(key_, (bytes32));
return $._keys[$._keyToOperator[key]].wasActiveAt(timestamp, key);
return $._keyData[key].status.wasActiveAt(timestamp);
}

/**
Expand All @@ -89,31 +90,29 @@ abstract contract KeyManager256 is KeyManager {
bytes32 key = abi.decode(key_, (bytes32));
uint48 timestamp = _now();

if ($._keyToOperator[key] != address(0)) {
if ($._keyData[key].value != address(0)) {
revert DuplicateKey();
}

// check if we have reached the max number of disabled keys
// this allow us to limit the number times we can change the key
if (key != ZERO_BYTES32 && $._keys[operator].length() > MAX_DISABLED_KEYS + 1) {
revert MaxDisabledKeysReached();
bytes32 prevKey = $._prevKey[operator];
if (prevKey != bytes32(0)) {
if (!$._keyData[prevKey].status.checkUnregister(timestamp, _SLASHING_WINDOW())) {
revert PreviousKeySlashable();
}
delete $._keyData[prevKey];
}

if ($._keys[operator].length() > 0) {
// try to remove disabled keys
bytes32 prevKey = $._keys[operator].array[0].value;
if ($._keys[operator].checkUnregister(timestamp, _SLASHING_WINDOW(), prevKey)) {
$._keys[operator].unregister(timestamp, _SLASHING_WINDOW(), prevKey);
delete $._keyToOperator[prevKey];
} else if ($._keys[operator].wasActiveAt(timestamp, prevKey)) {
$._keys[operator].pause(timestamp, prevKey);
}
bytes32 currentKey = $._key[operator];
if (currentKey != bytes32(0)) {
$._keyData[currentKey].status.disable(timestamp);
}

if (key != ZERO_BYTES32) {
// register the new key
$._keys[operator].register(timestamp, key);
$._keyToOperator[key] = operator;
$._prevKey[operator] = currentKey;
$._key[operator] = key;

if (key != bytes32(0)) {
$._keyData[key].value = operator;
$._keyData[key].status.set(timestamp);
}
}
}
Loading

0 comments on commit a65b247

Please sign in to comment.